now needs testing
This commit is contained in:
John Cupitt 2019-10-22 17:37:05 +01:00
parent ee3270f8e9
commit 60ce52512c
7 changed files with 212 additions and 182 deletions

View File

@ -86,6 +86,8 @@ typedef struct _VipsStreamClass {
GType vips_stream_get_type( void );
int vips_stream_open( VipsStream *stream );
int vips_stream_close( VipsStream *stream );
const char *vips_stream_name( VipsStream *stream );
#define VIPS_TYPE_STREAMI (vips_streami_get_type())
@ -201,6 +203,15 @@ typedef struct _VipsStreamiClass {
*/
void (*minimise)( VipsStreami * );
/* The opposite of minimise: restart anything that minimise shut down.
*/
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 );
@ -216,6 +227,7 @@ const void *vips_streami_map( VipsStreami *streami, size_t *length );
gint64 vips_streami_seek( VipsStreami *streami, gint64 offset, int whence );
int vips_streami_rewind( VipsStreami *streami );
void vips_streami_minimise( VipsStreami *streami );
int vips_streami_unminimise( VipsStreami *streami );
int vips_streami_decode( VipsStreami *streami );
unsigned char *vips_streami_sniff( VipsStreami *streami, size_t length );
gint64 vips_streami_size( VipsStreami *streami );

View File

@ -2185,8 +2185,7 @@ vips_image_new_from_buffer( const void *buf, size_t len,
* Returns: (transfer full): the new #VipsImage, or %NULL on error.
*/
VipsImage *
vips_image_new_from_stream( VipsStreamInput *input,
const char *option_string, ... )
vips_image_new_from_stream( VipsStreami *input, const char *option_string, ... )
{
const char *operation_name;
va_list ap;
@ -2726,7 +2725,7 @@ vips_image_write_to_buffer( VipsImage *in,
*/
int
vips_image_write_to_stream( VipsImage *in,
const char *suffix, VipsStreamOutput *output, ... )
const char *suffix, VipsStreamo *output, ... )
{
char filename[VIPS_PATH_MAX];
char option_string[VIPS_PATH_MAX];

View File

@ -330,8 +330,8 @@ vips_init( const char *argv0 )
extern GType write_thread_state_get_type( void );
extern GType sink_memory_thread_state_get_type( void );
extern GType render_thread_state_get_type( void );
extern GType vips_stream_input_get_type( void );
extern GType vips_stream_output_get_type( void );
extern GType vips_streami_get_type( void );
extern GType vips_streamo_get_type( void );
static gboolean started = FALSE;
static gboolean done = FALSE;
@ -446,8 +446,8 @@ vips_init( const char *argv0 )
(void) write_thread_state_get_type();
(void) sink_memory_thread_state_get_type();
(void) render_thread_state_get_type();
(void) vips_stream_input_get_type();
(void) vips_stream_output_get_type();
(void) vips_streami_get_type();
(void) vips_streamo_get_type();
vips__meta_init_types();
vips__interpolate_init();
im__format_init();

View File

@ -1908,10 +1908,10 @@ vips_object_set_argument_from_string( VipsObject *object,
vips__filename_split8( value, filename, option_string );
if( strcmp( "stdin", filename ) == 0 ) {
VipsStreamInput *input;
VipsStreami *input;
if( !(input =
vips_stream_input_new_from_descriptor( 0 )) )
vips_streami_new_from_descriptor( 0 )) )
return( -1 );
if( !(out = vips_image_new_from_stream( input,
option_string,
@ -1937,18 +1937,18 @@ vips_object_set_argument_from_string( VipsObject *object,
*/
g_object_unref( out );
}
else if( g_type_is_a( otype, VIPS_TYPE_STREAM_INPUT ) ) {
VipsStreamInput *input;
else if( g_type_is_a( otype, VIPS_TYPE_STREAMI ) ) {
VipsStreami *input;
if( !value ) {
vips_object_no_value( object, name );
return( -1 );
}
if( !(input = vips_stream_input_new_from_options( value )) )
if( !(input = vips_streami_new_from_options( value )) )
return( -1 );
g_value_init( &gvalue, VIPS_TYPE_STREAM_INPUT );
g_value_init( &gvalue, VIPS_TYPE_STREAMI );
g_value_set_object( &gvalue, input );
/* Setting gvalue will have upped @out's count again,
@ -2210,10 +2210,9 @@ vips_object_get_argument_to_string( VipsObject *object,
vips__filename_split8( arg, filename, option_string );
if( vips_isprefix( ".", filename ) ) {
VipsStreamOutput *output;
VipsStreamo *output;
if( !(output =
vips_stream_output_new_from_descriptor( 1 )) )
if( !(output = vips_streamo_new_from_descriptor( 1 )) )
return( -1 );
g_object_get( object, name, &in, NULL );
if( vips_image_write_to_stream( in,

View File

@ -79,26 +79,6 @@
G_DEFINE_ABSTRACT_TYPE( VipsStream, vips_stream, VIPS_TYPE_OBJECT );
static void
vips_stream_close( VipsStream *stream )
{
VIPS_DEBUG_MSG( "vips_stream_close:\n" );
if( stream->close_descriptor >= 0 ) {
VIPS_DEBUG_MSG( " close()\n" );
close( stream->close_descriptor );
stream->close_descriptor = -1;
}
if( stream->tracked_descriptor >= 0 ) {
VIPS_DEBUG_MSG( " vips_tracked_close()\n" );
vips_tracked_close( stream->tracked_descriptor );
stream->tracked_descriptor = -1;
}
stream->descriptor = -1;
}
static void
vips_stream_finalize( GObject *gobject )
{
@ -110,7 +90,20 @@ vips_stream_finalize( GObject *gobject )
VIPS_DEBUG_MSG( "\n" );
#endif /*VIPS_DEBUG*/
vips_stream_close( stream );
if( stream->tracked_descriptor >= 0 ) {
VIPS_DEBUG_MSG( " tracked_close()\n" );
vips_tracked_close( stream->tracked_descriptor );
stream->tracked_descriptor = -1;
stream->descriptor = -1;
}
if( stream->close_descriptor >= 0 ) {
VIPS_DEBUG_MSG( " close()\n" );
close( stream->close_descriptor );
stream->close_descriptor = -1;
stream->descriptor = -1;
}
VIPS_FREE( stream->filename );
G_OBJECT_CLASS( vips_stream_parent_class )->finalize( gobject );
@ -156,3 +149,4 @@ vips_stream_name( VipsStream *stream )
stream->filename :
VIPS_OBJECT( stream )->nickname );
}

View File

@ -36,12 +36,6 @@
* - filename encoding
* - 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 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
@ -200,43 +194,6 @@ vips_streami_finalize( GObject *gobject )
G_OBJECT_CLASS( vips_streami_parent_class )->finalize( gobject );
}
static int
vips_streami_open( VipsStreami *streami )
{
VipsStream *stream = VIPS_STREAM( streami );
vips_streami_sanity( streami );
if( stream->descriptor == -1 &&
stream->tracked_descriptor == -1 &&
stream->filename ) {
int fd;
if( (fd = vips_tracked_open( stream->filename,
MODE_READ )) == -1 ) {
vips_error_system( errno, vips_stream_name( stream ),
"%s", _( "unable to open for read" ) );
return( -1 );
}
stream->tracked_descriptor = fd;
stream->descriptor = fd;
if( streami->length == -1 &&
(streami->length = vips_file_length( fd )) == -1 )
return( -1 );
VIPS_DEBUG_MSG( "vips_streami_open: "
"restoring read position %zd\n",
streami->read_position );
if( vips__seek( stream->descriptor,
streami->read_position, SEEK_SET ) == -1 )
return( -1 );
}
return( 0 );
}
static int
vips_streami_build( VipsObject *object )
{
@ -257,7 +214,7 @@ vips_streami_build( VipsObject *object )
}
if( vips_object_argument_isset( object, "filename" ) &&
vips_streami_open( streami ) )
vips_streami_unminimise( streami ) )
return( -1 );
if( vips_object_argument_isset( object, "descriptor" ) ) {
@ -360,8 +317,128 @@ vips_streami_minimise_real( VipsStreami *streami )
if( stream->filename &&
stream->descriptor != -1 &&
!streami->is_pipe )
vips_stream_close( stream );
stream->tracked_descriptor != -1 &&
!streami->is_pipe ) {
VIPS_DEBUG_MSG( " tracked_close()\n" );
vips_tracked_close( stream->tracked_descriptor );
stream->tracked_descriptor = -1;
stream->descriptor = -1;
}
}
static int
vips_streami_unminimise_real( VipsStreami *streami )
{
VipsStream *stream = VIPS_STREAM( streami );
if( stream->descriptor == -1 &&
stream->tracked_descriptor == -1 &&
stream->filename ) {
int fd;
if( (fd = vips_tracked_open( stream->filename,
MODE_READ )) == -1 )
return( -1 );
stream->tracked_descriptor = fd;
stream->descriptor = fd;
if( streami->length == -1 &&
(streami->length = vips_file_length( fd )) == -1 )
return( -1 );
VIPS_DEBUG_MSG( "vips_streami_unminimise_real: "
"restoring read position %zd\n",
streami->read_position );
if( vips__seek( stream->descriptor,
streami->read_position, SEEK_SET ) == -1 )
return( -1 );
}
return( 0 );
}
/* Read a pipe to at least a position. -1 means read to end of stream. Does
* not chenge read_position.
*/
static int
vips_streami_pipe_read_to_position( VipsStreami *streami, gint64 target )
{
gint64 old_read_position;
unsigned char buffer[4096];
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;
/* TODO ... add something to prevent unbounded streams filling memory.
*/
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.
*/
streami->length = streami->header_bytes->len;
data = g_byte_array_free( streami->header_bytes, FALSE );
streami->header_bytes = NULL;
vips_blob_set( streami->blob,
(VipsCallbackFn) g_free, data, streami->length );
vips_streami_minimise( streami );
streami->is_pipe = FALSE;
vips_streami_sanity( streami );
return( 0 );
}
static gint64
vips_streami_size_real( VipsStreami *streami )
{
if( streami->length == -1 &&
streami->is_pipe &&
vips_streami_pipe_to_memory( streami ) )
return( -1 );
return( streami->length );
}
static void
@ -383,6 +460,8 @@ vips_streami_class_init( VipsStreamiClass *class )
class->map = vips_streami_map_real;
class->seek = vips_streami_seek_real;
class->minimise = vips_streami_minimise_real;
class->unminimise = vips_streami_unminimise_real;
class->size = vips_streami_size_real;
VIPS_ARG_BOXED( class, "blob", 3,
_( "Blob" ),
@ -621,85 +700,12 @@ vips_streami_read( VipsStreami *streami, void *buffer, size_t length )
return( bytes_read );
}
/* Read a pipe to at least a position. -1 means read to end of stream. Does
* not chenge read_position.
*/
static int
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_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;
/* TODO ... add something to prevent unbounded streams filling memory.
*/
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.
*/
streami->length = streami->header_bytes->len;
data = g_byte_array_free( streami->header_bytes, FALSE );
streami->header_bytes = NULL;
vips_blob_set( streami->blob,
(VipsCallbackFn) g_free, data, streami->length );
vips_stream_close( stream );
streami->is_pipe = FALSE;
vips_streami_sanity( streami );
return( 0 );
}
const void *
vips_streami_map( VipsStreami *streami, size_t *length_out )
{
VipsStreamiClass *class = VIPS_STREAMI_GET_CLASS( streami );
void *data;
const void *data;
ssize_t length;
VIPS_DEBUG_MSG( "vips_streami_map:\n" );
@ -715,20 +721,27 @@ vips_streami_map( VipsStreami *streami, size_t *length_out )
/* Memory source ... easy!
*/
if( streami->blob ) {
VIPS_DEBUG_MSG( " memory source\n" );
size_t unsigned_length;
data = vips_blob_get( streami->blob, &length );
/* Argh blobs are unsigned sizes.
*/
VIPS_DEBUG_MSG( " memory source\n" );
data = vips_blob_get( streami->blob, &unsigned_length );
length = VIPS_MIN( unsigned_length, G_MAXSSIZE );
}
else {
size_t unsigned_length;
/* A streami that supports mmap.
*/
VIPS_DEBUG_MSG( " mmaping source\n" );
if( !streami->baseaddr &&
!(streami->baseaddr =
class->map( streami, &streami->length )) )
class->map( streami, &unsigned_length )) )
return( NULL );
length = streami->length;
length = VIPS_MIN( unsigned_length, G_MAXSSIZE );
streami->length = length;
data = streami->baseaddr;
}
@ -828,6 +841,38 @@ vips_streami_minimise( VipsStreami *streami )
vips_streami_sanity( streami );
}
int
vips_streami_unminimise( VipsStreami *streami )
{
int result;
VipsStreamiClass *class = VIPS_STREAMI_GET_CLASS( streami );
vips_streami_sanity( streami );
result = class->unminimise( streami );
vips_streami_sanity( streami );
return( result );
}
gint64
vips_streami_size( VipsStreami *streami )
{
VipsStreamiClass *class = VIPS_STREAMI_GET_CLASS( streami );
gint64 size;
vips_streami_sanity( streami );
size = class->size( streami );
vips_streami_sanity( streami );
return( size );
}
int
vips_streami_decode( VipsStreami *streami )
{
@ -846,7 +891,7 @@ vips_streami_decode( VipsStreami *streami )
/* Make sure we are open, in case we've been minimised.
*/
if( vips_streami_open( streami ) )
if( vips_streami_unminimise( streami ) )
return( -1 );
vips_streami_sanity( streami );
@ -885,22 +930,3 @@ vips_streami_sniff( VipsStreami *streami, size_t length )
return( streami->sniff->data );
}
gint64
vips_streami_size( VipsStreami *streami )
{
VipsStream *stream = VIPS_STREAM( streami );
gint64 size;
vips_streami_sanity( streami );
if( streami->length == -1 &&
streami->is_pipe &&
vips_streami_pipe_to_memory( streami ) )
return( -1 );
vips_streami_sanity( streami );
return( streami->length );
}

View File

@ -316,7 +316,7 @@ vips_streamo_new_memory( void )
int
vips_streamo_write( VipsStreamo *streamo, const void *data, size_t length )
{
VipsStreamoClass *class = VIPS_STREAMO_GET_CLASS( stream );
VipsStreamoClass *class = VIPS_STREAMO_GET_CLASS( streamo );
VIPS_DEBUG_MSG( "vips_streamo_write: %zd bytes\n", length );