paste stream.c back in
revise stream.h, implement it next
This commit is contained in:
parent
be7c1404c3
commit
372bbc8020
@ -1,4 +1,5 @@
|
|||||||
pkginclude_HEADERS = \
|
pkginclude_HEADERS = \
|
||||||
|
stream.h \
|
||||||
basic.h \
|
basic.h \
|
||||||
type.h \
|
type.h \
|
||||||
gate.h \
|
gate.h \
|
||||||
|
213
libvips/include/vips/stream.h
Normal file
213
libvips/include/vips/stream.h
Normal file
@ -0,0 +1,213 @@
|
|||||||
|
/* A byte source/sink .. it can be a pipe, socket, or perhaps a node.js stream.
|
||||||
|
*
|
||||||
|
* J.Cupitt, 19/6/14
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef VIPS_STREAM_H
|
||||||
|
#define VIPS_STREAM_H
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif /*__cplusplus*/
|
||||||
|
|
||||||
|
#define VIPS_TYPE_STREAM (vips_stream_get_type())
|
||||||
|
#define VIPS_STREAM( obj ) \
|
||||||
|
(G_TYPE_CHECK_INSTANCE_CAST( (obj), \
|
||||||
|
VIPS_TYPE_STREAM, VipsStream ))
|
||||||
|
#define VIPS_STREAM_CLASS( klass ) \
|
||||||
|
(G_TYPE_CHECK_CLASS_CAST( (klass), \
|
||||||
|
VIPS_TYPE_STREAM, VipsStreamClass))
|
||||||
|
#define VIPS_IS_STREAM( obj ) \
|
||||||
|
(G_TYPE_CHECK_INSTANCE_TYPE( (obj), VIPS_TYPE_STREAM ))
|
||||||
|
#define VIPS_IS_STREAM_CLASS( klass ) \
|
||||||
|
(G_TYPE_CHECK_CLASS_TYPE( (klass), VIPS_TYPE_STREAM ))
|
||||||
|
#define VIPS_STREAM_GET_CLASS( obj ) \
|
||||||
|
(G_TYPE_INSTANCE_GET_CLASS( (obj), \
|
||||||
|
VIPS_TYPE_STREAM, VipsStreamClass ))
|
||||||
|
|
||||||
|
/* Communicate with something like a socket or pipe.
|
||||||
|
*/
|
||||||
|
typedef struct _VipsStream {
|
||||||
|
VipsObject parent_object;
|
||||||
|
|
||||||
|
/*< private >*/
|
||||||
|
|
||||||
|
/* Read/write this fd if connected to a system pipe/socket. Override
|
||||||
|
* ::read() and ::write() to do something else.
|
||||||
|
*/
|
||||||
|
int descriptor;
|
||||||
|
|
||||||
|
/* If descriptor is a file, the filename we opened. Handy for error
|
||||||
|
* messages.
|
||||||
|
*/
|
||||||
|
const char *filename;
|
||||||
|
|
||||||
|
} VipsStream;
|
||||||
|
|
||||||
|
typedef struct _VipsStreamClass {
|
||||||
|
VipsObjectClass parent_class;
|
||||||
|
|
||||||
|
} VipsStreamClass;
|
||||||
|
|
||||||
|
GType vips_stream_get_type( void );
|
||||||
|
|
||||||
|
void vips_stream_attach( VipsStream *stream );
|
||||||
|
|
||||||
|
#define VIPS_TYPE_STREAM_INPUT (vips_stream_input_get_type())
|
||||||
|
#define VIPS_STREAM_INPUT( obj ) \
|
||||||
|
(G_TYPE_CHECK_INSTANCE_CAST( (obj), \
|
||||||
|
VIPS_TYPE_STREAM_INPUT, VipsStreamInput ))
|
||||||
|
#define VIPS_STREAM_INPUT_CLASS( klass ) \
|
||||||
|
(G_TYPE_CHECK_CLASS_CAST( (klass), \
|
||||||
|
VIPS_TYPE_STREAM_INPUT, VipsStreamInputClass))
|
||||||
|
#define VIPS_IS_STREAM_INPUT( obj ) \
|
||||||
|
(G_TYPE_CHECK_INSTANCE_TYPE( (obj), VIPS_TYPE_STREAM_INPUT ))
|
||||||
|
#define VIPS_IS_STREAM_INPUT_CLASS( klass ) \
|
||||||
|
(G_TYPE_CHECK_CLASS_TYPE( (klass), VIPS_TYPE_STREAM_INPUT ))
|
||||||
|
#define VIPS_STREAM_INPUT_GET_CLASS( obj ) \
|
||||||
|
(G_TYPE_INSTANCE_GET_CLASS( (obj), \
|
||||||
|
VIPS_TYPE_STREAM_INPUT, VipsStreamInputClass ))
|
||||||
|
|
||||||
|
/* Read from something like a socket, file or memory area and present the data
|
||||||
|
* with a simple seek / read interface.
|
||||||
|
*
|
||||||
|
* During the header phase, we save data from unseekable sources in a buffer
|
||||||
|
* so readers can rewind and read again. We don't buffer data during the
|
||||||
|
* decode stage.
|
||||||
|
*/
|
||||||
|
typedef struct _VipsStreamInput {
|
||||||
|
VipsStream parent_object;
|
||||||
|
|
||||||
|
/* We have two phases:
|
||||||
|
*
|
||||||
|
* During the header phase, we save bytes read from the input (if this
|
||||||
|
* is an unseekable source) so that we can rewind and try again, if
|
||||||
|
* necessary.
|
||||||
|
*
|
||||||
|
* Once we reach decode pahse, we no longer support seek and the
|
||||||
|
* buffer of saved data is discarded.
|
||||||
|
*/
|
||||||
|
gboolean decode;
|
||||||
|
|
||||||
|
/* TRUE is this input source suports seek. If not, then we save data
|
||||||
|
* read during header phase in a buffer so we can rewind.
|
||||||
|
*/
|
||||||
|
gboolean seekable;
|
||||||
|
|
||||||
|
/*< private >*/
|
||||||
|
|
||||||
|
/* The current read point.
|
||||||
|
*/
|
||||||
|
off_t read_position;
|
||||||
|
|
||||||
|
/* Save data read during header phase here. If we rewind and try
|
||||||
|
* again, serve data from this until it runs out.
|
||||||
|
*/
|
||||||
|
GByteArray *buffer;
|
||||||
|
|
||||||
|
/* For a memory source, the blob we read from.
|
||||||
|
*/
|
||||||
|
VipsBlob *blob;
|
||||||
|
|
||||||
|
/* Set on EOF.
|
||||||
|
*/
|
||||||
|
gboolean eof;
|
||||||
|
|
||||||
|
} VipsStreamInput;
|
||||||
|
|
||||||
|
typedef struct _VipsStreamInputClass {
|
||||||
|
VipsStreamClass parent_class;
|
||||||
|
|
||||||
|
/* Subclasses can define these to implement other input methods.
|
||||||
|
*/
|
||||||
|
ssize_t (*read)( VipsStreamInput *, unsigned char *, size_t );
|
||||||
|
void (*rewind)( VipsStreamInput * );
|
||||||
|
|
||||||
|
} VipsStreamInputClass;
|
||||||
|
|
||||||
|
GType vips_stream_input_get_type( void );
|
||||||
|
|
||||||
|
VipsStreamInput *vips_stream_input_new_from_descriptor( int descriptor );
|
||||||
|
VipsStreamInput *vips_stream_input_new_from_filename( const char *filename );
|
||||||
|
VipsStreamInput *vips_stream_input_new_from_blob( VipsBlob *blob );
|
||||||
|
VipsStreamInput *vips_stream_input_new_from_buffer( void *buf, size_t len );
|
||||||
|
|
||||||
|
ssize_t vips_stream_input_read( VipsStreamInput *input,
|
||||||
|
unsigned char *buffer, size_t length );
|
||||||
|
void vips_stream_input_rewind( VipsStreamInput *input );
|
||||||
|
void vips_stream_input_decode( VipsStreamInput *input );
|
||||||
|
gboolean vips_stream_input_eof( VipsStreamInput *input );
|
||||||
|
unsigned char *vips_stream_input_sniff( VipsStreamInput *input, size_t length );
|
||||||
|
|
||||||
|
#define VIPS_TYPE_STREAM_OUTPUT (vips_stream_output_get_type())
|
||||||
|
#define VIPS_STREAM_OUTPUT( obj ) \
|
||||||
|
(G_TYPE_CHECK_INSTANCE_CAST( (obj), \
|
||||||
|
VIPS_TYPE_STREAM_OUTPUT, VipsStreamOutput ))
|
||||||
|
#define VIPS_STREAM_OUTPUT_CLASS( klass ) \
|
||||||
|
(G_TYPE_CHECK_CLASS_CAST( (klass), \
|
||||||
|
VIPS_TYPE_STREAM_OUTPUT, VipsStreamOutputClass))
|
||||||
|
#define VIPS_IS_STREAM_OUTPUT( obj ) \
|
||||||
|
(G_TYPE_CHECK_INSTANCE_TYPE( (obj), VIPS_TYPE_STREAM_OUTPUT ))
|
||||||
|
#define VIPS_IS_STREAM_OUTPUT_CLASS( klass ) \
|
||||||
|
(G_TYPE_CHECK_CLASS_TYPE( (klass), VIPS_TYPE_STREAM_OUTPUT ))
|
||||||
|
#define VIPS_STREAM_OUTPUT_GET_CLASS( obj ) \
|
||||||
|
(G_TYPE_INSTANCE_GET_CLASS( (obj), \
|
||||||
|
VIPS_TYPE_STREAM_OUTPUT, VipsStreamOutputClass ))
|
||||||
|
|
||||||
|
/* Read or output to something like a socket or pipe.
|
||||||
|
*/
|
||||||
|
typedef struct _VipsStreamOutput {
|
||||||
|
VipsStream parent_object;
|
||||||
|
|
||||||
|
/*< private >*/
|
||||||
|
|
||||||
|
} VipsStreamOutput;
|
||||||
|
|
||||||
|
typedef struct _VipsStreamOutputClass {
|
||||||
|
VipsStreamClass parent_class;
|
||||||
|
|
||||||
|
/* If defined, output some bytes with this. Otherwise use write().
|
||||||
|
*/
|
||||||
|
ssize_t (*write)( VipsStreamOutput *, const unsigned char *, size_t );
|
||||||
|
|
||||||
|
} VipsStreamOutputClass;
|
||||||
|
|
||||||
|
GType vips_stream_output_get_type( void );
|
||||||
|
|
||||||
|
VipsStreamOutput *vips_stream_output_new_from_descriptor( int descriptor );
|
||||||
|
VipsStreamOutput *vips_stream_output_new_from_filename( const char *filename );
|
||||||
|
int vips_stream_output_write( VipsStreamOutput *stream,
|
||||||
|
const unsigned char *buffer, size_t buffer_size );
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif /*__cplusplus*/
|
||||||
|
|
||||||
|
#endif /*VIPS_STREAM_H*/
|
@ -113,6 +113,7 @@ extern "C" {
|
|||||||
#include <vips/object.h>
|
#include <vips/object.h>
|
||||||
#include <vips/type.h>
|
#include <vips/type.h>
|
||||||
#include <vips/gate.h>
|
#include <vips/gate.h>
|
||||||
|
#include <vips/stream.h>
|
||||||
|
|
||||||
#include <vips/version.h>
|
#include <vips/version.h>
|
||||||
#include <vips/rect.h>
|
#include <vips/rect.h>
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
noinst_LTLIBRARIES = libiofuncs.la
|
noinst_LTLIBRARIES = libiofuncs.la
|
||||||
|
|
||||||
libiofuncs_la_SOURCES = \
|
libiofuncs_la_SOURCES = \
|
||||||
|
stream.c \
|
||||||
dbuf.c \
|
dbuf.c \
|
||||||
reorder.c \
|
reorder.c \
|
||||||
vipsmarshal.h \
|
vipsmarshal.h \
|
||||||
|
683
libvips/iofuncs/stream.c
Normal file
683
libvips/iofuncs/stream.c
Normal file
@ -0,0 +1,683 @@
|
|||||||
|
/* A byte source/sink .. it can be a pipe, file descriptor, memory area,
|
||||||
|
* socket, or perhaps a node.js stream.
|
||||||
|
*
|
||||||
|
* J.Cupitt, 19/6/14
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
#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 <vips/vips.h>
|
||||||
|
#include <vips/debug.h>
|
||||||
|
|
||||||
|
/* Try to make an O_BINARY ... sometimes need the leading '_'.
|
||||||
|
*/
|
||||||
|
#ifdef BINARY_OPEN
|
||||||
|
#ifndef O_BINARY
|
||||||
|
#ifdef _O_BINARY
|
||||||
|
#define O_BINARY _O_BINARY
|
||||||
|
#endif /*_O_BINARY*/
|
||||||
|
#endif /*!O_BINARY*/
|
||||||
|
#endif /*BINARY_OPEN*/
|
||||||
|
|
||||||
|
/* If we have O_BINARY, add it to a mode flags set.
|
||||||
|
*/
|
||||||
|
#ifdef O_BINARY
|
||||||
|
#define BINARYIZE(M) ((M) | O_BINARY)
|
||||||
|
#else /*!O_BINARY*/
|
||||||
|
#define BINARYIZE(M) (M)
|
||||||
|
#endif /*O_BINARY*/
|
||||||
|
|
||||||
|
#define MODE_READ BINARYIZE (O_RDONLY)
|
||||||
|
#define MODE_READWRITE BINARYIZE (O_RDWR)
|
||||||
|
#define MODE_WRITE BINARYIZE (O_WRONLY | O_CREAT | O_TRUNC)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SECTION: stream
|
||||||
|
* @short_description: a source/sink of bytes, perhaps a network socket
|
||||||
|
* @stability: Stable
|
||||||
|
* @see_also: <link linkend="libvips-foreign">foreign</link>
|
||||||
|
* @include: vips/vips.h
|
||||||
|
*
|
||||||
|
* A #VipsStream is a source or sink of bytes for something like jpeg loading.
|
||||||
|
* It can be connected to a network socket, for example, or perhaps a node.js
|
||||||
|
* stream, or to an area of memory.
|
||||||
|
*
|
||||||
|
* Subclass to add other input sources.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* VipsStream:
|
||||||
|
*
|
||||||
|
* A #VipsStream is a source of bytes for something like jpeg loading. It can
|
||||||
|
* be connected to a network socket, for example.
|
||||||
|
*/
|
||||||
|
|
||||||
|
G_DEFINE_ABSTRACT_TYPE( VipsStream, vips_stream, VIPS_TYPE_OBJECT );
|
||||||
|
|
||||||
|
static void
|
||||||
|
vips_stream_finalize( GObject *gobject )
|
||||||
|
{
|
||||||
|
VipsStream *stream = (VipsStream *) gobject;
|
||||||
|
|
||||||
|
#ifdef VIPS_DEBUG
|
||||||
|
VIPS_DEBUG_MSG( "vips_stream_finalize: " );
|
||||||
|
vips_object_print_name( VIPS_OBJECT( gobject ) );
|
||||||
|
VIPS_DEBUG_MSG( "\n" );
|
||||||
|
#endif /*VIPS_DEBUG*/
|
||||||
|
|
||||||
|
if( stream->descriptor >= 0 ) {
|
||||||
|
vips_tracked_close( stream->descriptor );
|
||||||
|
stream->descriptor = -1;
|
||||||
|
}
|
||||||
|
VIPS_FREE( stream->filename );
|
||||||
|
|
||||||
|
G_OBJECT_CLASS( vips_stream_parent_class )->finalize( gobject );
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
vips_stream_class_init( VipsStreamClass *class )
|
||||||
|
{
|
||||||
|
GObjectClass *gobject_class = G_OBJECT_CLASS( class );
|
||||||
|
|
||||||
|
gobject_class->finalize = vips_stream_finalize;
|
||||||
|
gobject_class->set_property = vips_object_set_property;
|
||||||
|
gobject_class->get_property = vips_object_get_property;
|
||||||
|
|
||||||
|
VIPS_ARG_INT( class, "descriptor", 1,
|
||||||
|
_( "Descriptor" ),
|
||||||
|
_( "File descriptor for read or write" ),
|
||||||
|
VIPS_ARGUMENT_OPTIONAL_INPUT,
|
||||||
|
G_STRUCT_OFFSET( VipsStream, descriptor ),
|
||||||
|
-1, 1000000000, 0 );
|
||||||
|
|
||||||
|
VIPS_ARG_STRING( class, "filename", 2,
|
||||||
|
_( "Filename" ),
|
||||||
|
_( "Name of file to open" ),
|
||||||
|
VIPS_ARGUMENT_OPTIONAL_INPUT,
|
||||||
|
G_STRUCT_OFFSET( VipsStream, filename ),
|
||||||
|
NULL );
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
vips_stream_init( VipsStream *stream )
|
||||||
|
{
|
||||||
|
stream->descriptor = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
vips_stream_attach( VipsStream *stream )
|
||||||
|
{
|
||||||
|
#ifdef VIPS_DEBUG
|
||||||
|
VIPS_DEBUG_MSG( "vips_stream_attach: " );
|
||||||
|
vips_object_print_name( VIPS_OBJECT( stream ) );
|
||||||
|
VIPS_DEBUG_MSG( "\n" );
|
||||||
|
#endif /*VIPS_DEBUG*/
|
||||||
|
|
||||||
|
g_assert( !stream->attached );
|
||||||
|
stream->attached = TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
G_DEFINE_TYPE( VipsStreamInput, vips_stream_input, VIPS_TYPE_STREAM );
|
||||||
|
|
||||||
|
static void
|
||||||
|
vips_stream_input_finalize( GObject *gobject )
|
||||||
|
{
|
||||||
|
VipsStreamInput *stream = (VipsStreamInput *) gobject;
|
||||||
|
|
||||||
|
VIPS_FREE( stream->buffer );
|
||||||
|
|
||||||
|
G_OBJECT_CLASS( vips_stream_input_parent_class )->finalize( gobject );
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
vips_stream_input_build( VipsObject *object )
|
||||||
|
{
|
||||||
|
VipsStreamInput *stream = VIPS_STREAM_INPUT( object );
|
||||||
|
|
||||||
|
VIPS_DEBUG_MSG( "vips_stream_input_build: %p\n", stream );
|
||||||
|
|
||||||
|
if( VIPS_OBJECT_CLASS( vips_stream_input_parent_class )->
|
||||||
|
build( object ) )
|
||||||
|
return( -1 );
|
||||||
|
|
||||||
|
if( vips_object_argument_isset( object, "filename" ) &&
|
||||||
|
!vips_object_argument_isset( object, "descriptor" ) ) {
|
||||||
|
const char *filename = VIPS_STREAM( stream )->filename;
|
||||||
|
|
||||||
|
int fd;
|
||||||
|
|
||||||
|
if( (fd = vips_tracked_open( filename, MODE_READ )) == -1 ) {
|
||||||
|
vips_error_system( errno, filename,
|
||||||
|
"%s", _( "unable to open for read" ) );
|
||||||
|
return( -1 );
|
||||||
|
}
|
||||||
|
|
||||||
|
g_object_set( object, "descriptor", fd, NULL );
|
||||||
|
}
|
||||||
|
|
||||||
|
g_assert( !stream->buffer );
|
||||||
|
g_assert( stream->buffer_size > 0 &&
|
||||||
|
stream->buffer_size < 1000000 );
|
||||||
|
stream->buffer = g_new0( unsigned char, stream->buffer_size );
|
||||||
|
stream->next_byte = NULL;
|
||||||
|
stream->bytes_available = 0;
|
||||||
|
|
||||||
|
return( 0 );
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
vips_stream_input_class_init( VipsStreamInputClass *class )
|
||||||
|
{
|
||||||
|
GObjectClass *gobject_class = G_OBJECT_CLASS( class );
|
||||||
|
VipsObjectClass *vobject_class = VIPS_OBJECT_CLASS( class );
|
||||||
|
|
||||||
|
gobject_class->finalize = vips_stream_input_finalize;
|
||||||
|
gobject_class->set_property = vips_object_set_property;
|
||||||
|
gobject_class->get_property = vips_object_get_property;
|
||||||
|
|
||||||
|
vobject_class->build = vips_stream_input_build;
|
||||||
|
|
||||||
|
VIPS_ARG_INT( class, "buffer_size", 2,
|
||||||
|
_( "Buffer size" ),
|
||||||
|
_( "Size of input buffer" ),
|
||||||
|
VIPS_ARGUMENT_OPTIONAL_INPUT,
|
||||||
|
G_STRUCT_OFFSET( VipsStreamInput, buffer_size ),
|
||||||
|
1, 10000000, 4096 );
|
||||||
|
|
||||||
|
VIPS_ARG_BOXED( class, "blob", 3,
|
||||||
|
_( "Blob" ),
|
||||||
|
_( "Memory area to read from" ),
|
||||||
|
VIPS_ARGUMENT_OPTIONAL_INPUT,
|
||||||
|
G_STRUCT_OFFSET( VipsStreamInput, blob ),
|
||||||
|
VIPS_TYPE_BLOB );
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
vips_stream_input_init( VipsStreamInput *stream )
|
||||||
|
{
|
||||||
|
stream->buffer_size = 4096;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* vips_stream_input_new_from_descriptor:
|
||||||
|
* @descriptor: read from this file descriptor
|
||||||
|
*
|
||||||
|
* Create a stream attached to a file descriptor. @descriptor is closed when
|
||||||
|
* the #VipsStream is finalized.
|
||||||
|
*
|
||||||
|
* #VipsStream s start out empty, you need to call
|
||||||
|
* vips_stream_input_refill() to fill them with bytes.
|
||||||
|
*
|
||||||
|
* See also: vips_stream_input_refill().
|
||||||
|
*
|
||||||
|
* Returns: a new #VipsStream
|
||||||
|
*/
|
||||||
|
VipsStreamInput *
|
||||||
|
vips_stream_input_new_from_descriptor( int descriptor )
|
||||||
|
{
|
||||||
|
VipsStreamInput *stream;
|
||||||
|
|
||||||
|
VIPS_DEBUG_MSG( "vips_stream_input_new_from_descriptor: %d\n",
|
||||||
|
descriptor );
|
||||||
|
|
||||||
|
stream = VIPS_STREAM_INPUT(
|
||||||
|
g_object_new( VIPS_TYPE_STREAM_INPUT,
|
||||||
|
"descriptor", descriptor,
|
||||||
|
NULL ) );
|
||||||
|
|
||||||
|
if( vips_object_build( VIPS_OBJECT( stream ) ) ) {
|
||||||
|
VIPS_UNREF( stream );
|
||||||
|
return( NULL );
|
||||||
|
}
|
||||||
|
|
||||||
|
return( stream );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* vips_stream_input_new_from_filename:
|
||||||
|
* @descriptor: read from this filename
|
||||||
|
*
|
||||||
|
* Create a stream attached to a file.
|
||||||
|
*
|
||||||
|
* #VipsStream s start out empty, you need to call
|
||||||
|
* vips_stream_input_refill() to fill them with bytes.
|
||||||
|
*
|
||||||
|
* See also: vips_stream_input_refill().
|
||||||
|
*
|
||||||
|
* Returns: a new #VipsStream
|
||||||
|
*/
|
||||||
|
VipsStreamInput *
|
||||||
|
vips_stream_input_new_from_filename( const char *filename )
|
||||||
|
{
|
||||||
|
VipsStreamInput *stream;
|
||||||
|
|
||||||
|
VIPS_DEBUG_MSG( "vips_stream_input_new_from_filename: %s\n",
|
||||||
|
filename );
|
||||||
|
|
||||||
|
stream = VIPS_STREAM_INPUT(
|
||||||
|
g_object_new( VIPS_TYPE_STREAM_INPUT,
|
||||||
|
"filename", filename,
|
||||||
|
NULL ) );
|
||||||
|
|
||||||
|
if( vips_object_build( VIPS_OBJECT( stream ) ) ) {
|
||||||
|
VIPS_UNREF( stream );
|
||||||
|
return( NULL );
|
||||||
|
}
|
||||||
|
|
||||||
|
return( stream );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* vips_stream_input_new_from_blob:
|
||||||
|
* @blob: memory area to load
|
||||||
|
*
|
||||||
|
* Create a stream attached to an area of memory.
|
||||||
|
*
|
||||||
|
* #VipsStream s start out empty, you need to call
|
||||||
|
* vips_stream_input_refill() to fill them with bytes.
|
||||||
|
*
|
||||||
|
* See also: vips_stream_input_refill().
|
||||||
|
*
|
||||||
|
* Returns: a new #VipsStream
|
||||||
|
*/
|
||||||
|
VipsStreamInput *
|
||||||
|
vips_stream_input_new_from_blob( VipsBlob *blob )
|
||||||
|
{
|
||||||
|
VipsStreamInput *stream;
|
||||||
|
|
||||||
|
VIPS_DEBUG_MSG( "vips_stream_input_new_from_blob: %p\n", blob );
|
||||||
|
|
||||||
|
stream = VIPS_STREAM_INPUT(
|
||||||
|
g_object_new( VIPS_TYPE_STREAM_INPUT,
|
||||||
|
"blob", blob,
|
||||||
|
NULL ) );
|
||||||
|
|
||||||
|
if( vips_object_build( VIPS_OBJECT( stream ) ) ) {
|
||||||
|
VIPS_UNREF( stream );
|
||||||
|
return( NULL );
|
||||||
|
}
|
||||||
|
|
||||||
|
return( stream );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* vips_stream_input_new_from_buffer:
|
||||||
|
* @buf: memory area to load
|
||||||
|
* @len: size of memory area
|
||||||
|
*
|
||||||
|
* Create a stream attached to an area of memory.
|
||||||
|
*
|
||||||
|
* You must not free @buf while the stream is active.
|
||||||
|
*
|
||||||
|
* #VipsStream s start out empty, you need to call
|
||||||
|
* vips_stream_input_refill() to fill them with bytes.
|
||||||
|
*
|
||||||
|
* See also: vips_stream_input_refill().
|
||||||
|
*
|
||||||
|
* Returns: a new #VipsStream
|
||||||
|
*/
|
||||||
|
VipsStreamInput *
|
||||||
|
vips_stream_input_new_from_buffer( void *buf, size_t len )
|
||||||
|
{
|
||||||
|
VipsStreamInput *stream;
|
||||||
|
VipsBlob *blob;
|
||||||
|
|
||||||
|
VIPS_DEBUG_MSG( "vips_stream_input_new_from_buffer: %p, len = %zd\n",
|
||||||
|
buf, len );
|
||||||
|
|
||||||
|
/* We don't take a copy of the data or free it.
|
||||||
|
*/
|
||||||
|
blob = vips_blob_new( NULL, buf, len );
|
||||||
|
|
||||||
|
stream = vips_stream_input_new_from_blob( blob );
|
||||||
|
|
||||||
|
vips_area_unref( VIPS_AREA( blob ) );
|
||||||
|
|
||||||
|
return( stream );
|
||||||
|
}
|
||||||
|
|
||||||
|
static ssize_t
|
||||||
|
vips_stream_input_read( VipsStreamInput *stream,
|
||||||
|
unsigned char *buffer, size_t buffer_size )
|
||||||
|
{
|
||||||
|
VipsStreamInputClass *class = VIPS_STREAM_INPUT_GET_CLASS( stream );
|
||||||
|
|
||||||
|
ssize_t len;
|
||||||
|
|
||||||
|
if( class->read )
|
||||||
|
len = class->read( stream, buffer, buffer_size );
|
||||||
|
else if( VIPS_STREAM( stream )->descriptor > 0 )
|
||||||
|
len = read( VIPS_STREAM( stream )->descriptor,
|
||||||
|
buffer, buffer_size );
|
||||||
|
else
|
||||||
|
g_assert( 0 );
|
||||||
|
|
||||||
|
return( len );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* vips_stream_input_refill:
|
||||||
|
* @stream: fill the stream buffer
|
||||||
|
*
|
||||||
|
* Reads data into the stream buffer.
|
||||||
|
*
|
||||||
|
* Returns: 0 on success, -1 on error or EOF.
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
vips_stream_input_refill( VipsStreamInput *stream )
|
||||||
|
{
|
||||||
|
ssize_t len;
|
||||||
|
|
||||||
|
/* If this is a memory source, we just set next_byte on the first
|
||||||
|
* call and we're done.
|
||||||
|
*
|
||||||
|
* If we're not attached, we can read even when the buffer isn't
|
||||||
|
* empty. Just move the unused bytes down and top up.
|
||||||
|
*
|
||||||
|
* If we're attached, we don't own the next_byte and bytes_available
|
||||||
|
* values (they are run by the load library) so we can't do this.
|
||||||
|
*
|
||||||
|
* We need to be able to refill the unattached buffer so we can do
|
||||||
|
* file format sniffing.
|
||||||
|
*/
|
||||||
|
if( stream->blob ) {
|
||||||
|
/* On the first call we read the whole of the input blob. On
|
||||||
|
* the second call, we EOF.
|
||||||
|
*/
|
||||||
|
if( stream->next_byte )
|
||||||
|
len = 0;
|
||||||
|
else
|
||||||
|
stream->next_byte = (void *)
|
||||||
|
vips_blob_get( stream->blob, (size_t *) &len );
|
||||||
|
}
|
||||||
|
else if( !VIPS_STREAM( stream )->attached ) {
|
||||||
|
memmove( stream->buffer, stream->next_byte,
|
||||||
|
stream->bytes_available );
|
||||||
|
stream->next_byte = stream->buffer;
|
||||||
|
|
||||||
|
len = vips_stream_input_read( stream,
|
||||||
|
stream->next_byte,
|
||||||
|
stream->buffer_size - stream->bytes_available );
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
len = vips_stream_input_read( stream,
|
||||||
|
stream->buffer, stream->buffer_size );
|
||||||
|
stream->next_byte = stream->buffer;
|
||||||
|
|
||||||
|
/* This is incremented below, after we check the return value.
|
||||||
|
*/
|
||||||
|
stream->bytes_available = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef VIPS_DEBUG
|
||||||
|
if( len > 0 )
|
||||||
|
VIPS_DEBUG_MSG( "vips_stream_input_refill: "
|
||||||
|
"read %zd bytes\n", len );
|
||||||
|
#endif /*VIPS_DEBUG*/
|
||||||
|
|
||||||
|
if( len <= 0 ) {
|
||||||
|
stream->eof = TRUE;
|
||||||
|
|
||||||
|
if( len < 0 )
|
||||||
|
vips_error_system( errno, "read",
|
||||||
|
"%s", _( "read error" ) );
|
||||||
|
|
||||||
|
return( -1 );
|
||||||
|
}
|
||||||
|
|
||||||
|
stream->bytes_available += len;
|
||||||
|
|
||||||
|
return( 0 );
|
||||||
|
}
|
||||||
|
|
||||||
|
gboolean
|
||||||
|
vips_stream_input_eof( VipsStreamInput *stream )
|
||||||
|
{
|
||||||
|
if( !stream->eof &&
|
||||||
|
stream->bytes_available == 0 &&
|
||||||
|
!VIPS_STREAM( stream )->attached )
|
||||||
|
vips_stream_input_refill( stream );
|
||||||
|
|
||||||
|
return( stream->eof );
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
vips_stream_input_detach( VipsStreamInput *stream,
|
||||||
|
unsigned char *next_byte, size_t bytes_available )
|
||||||
|
{
|
||||||
|
VIPS_DEBUG_MSG( "vips_stream_input_detach:\n" );
|
||||||
|
|
||||||
|
g_assert( VIPS_STREAM( stream )->attached );
|
||||||
|
VIPS_STREAM( stream )->attached = FALSE;
|
||||||
|
|
||||||
|
stream->next_byte = next_byte;
|
||||||
|
stream->bytes_available = bytes_available;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* vips_stream_input_sniff:
|
||||||
|
* @bytes: number of bytes to sniff
|
||||||
|
*
|
||||||
|
* Return a pointer to the start of the next @bytes bytes. This can only be
|
||||||
|
* used in detached mode.
|
||||||
|
*/
|
||||||
|
unsigned char *
|
||||||
|
vips_stream_input_sniff( VipsStreamInput *stream, int bytes )
|
||||||
|
{
|
||||||
|
if( VIPS_STREAM( stream )->attached ) {
|
||||||
|
g_warning( "%s", _( "cannot sniff attached streams" ) );
|
||||||
|
return( NULL );
|
||||||
|
}
|
||||||
|
|
||||||
|
while( stream->bytes_available < bytes )
|
||||||
|
if( vips_stream_input_refill( stream ) ) {
|
||||||
|
g_warning( "%s",
|
||||||
|
_( "not enough bytes in stream to sniff" ) );
|
||||||
|
return( NULL );
|
||||||
|
}
|
||||||
|
|
||||||
|
return( stream->next_byte );
|
||||||
|
}
|
||||||
|
|
||||||
|
G_DEFINE_TYPE( VipsStreamOutput, vips_stream_output, VIPS_TYPE_STREAM );
|
||||||
|
|
||||||
|
static int
|
||||||
|
vips_stream_output_build( VipsObject *object )
|
||||||
|
{
|
||||||
|
VipsStreamOutput *stream = VIPS_STREAM_OUTPUT( object );
|
||||||
|
|
||||||
|
VIPS_DEBUG_MSG( "vips_stream_output_build: %p\n", stream );
|
||||||
|
|
||||||
|
if( VIPS_OBJECT_CLASS( vips_stream_output_parent_class )->
|
||||||
|
build( object ) )
|
||||||
|
return( -1 );
|
||||||
|
|
||||||
|
if( vips_object_argument_isset( object, "filename" ) &&
|
||||||
|
!vips_object_argument_isset( object, "descriptor" ) ) {
|
||||||
|
const char *filename = VIPS_STREAM( stream )->filename;
|
||||||
|
|
||||||
|
int fd;
|
||||||
|
|
||||||
|
if( (fd = vips_tracked_open( filename, MODE_WRITE )) == -1 ) {
|
||||||
|
vips_error_system( errno, filename,
|
||||||
|
"%s", _( "unable to open for write" ) );
|
||||||
|
return( -1 );
|
||||||
|
}
|
||||||
|
|
||||||
|
g_object_set( object, "descriptor", fd, NULL );
|
||||||
|
}
|
||||||
|
|
||||||
|
return( 0 );
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
vips_stream_output_class_init( VipsStreamOutputClass *class )
|
||||||
|
{
|
||||||
|
VipsObjectClass *vobject_class = VIPS_OBJECT_CLASS( class );
|
||||||
|
|
||||||
|
vobject_class->build = vips_stream_output_build;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
vips_stream_output_init( VipsStreamOutput *stream )
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* vips_stream_output_new_from_descriptor:
|
||||||
|
* @descriptor: write to this file descriptor
|
||||||
|
*
|
||||||
|
* Create a stream attached to a file descriptor.
|
||||||
|
* @descriptor is closed when
|
||||||
|
* the #VipsStream is finalized.
|
||||||
|
*
|
||||||
|
* See also: vips_stream_output_write().
|
||||||
|
*
|
||||||
|
* Returns: a new #VipsStream
|
||||||
|
*/
|
||||||
|
VipsStreamOutput *
|
||||||
|
vips_stream_output_new_from_descriptor( int descriptor )
|
||||||
|
{
|
||||||
|
VipsStreamOutput *stream;
|
||||||
|
|
||||||
|
VIPS_DEBUG_MSG( "vips_stream_output_new_from_descriptor: %d\n",
|
||||||
|
descriptor );
|
||||||
|
|
||||||
|
stream = VIPS_STREAM_OUTPUT(
|
||||||
|
g_object_new( VIPS_TYPE_STREAM_OUTPUT,
|
||||||
|
"descriptor", descriptor,
|
||||||
|
"filename", "descriptor",
|
||||||
|
NULL ) );
|
||||||
|
|
||||||
|
if( vips_object_build( VIPS_OBJECT( stream ) ) ) {
|
||||||
|
VIPS_UNREF( stream );
|
||||||
|
return( NULL );
|
||||||
|
}
|
||||||
|
|
||||||
|
return( stream );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* vips_stream_output_new_from_filename:
|
||||||
|
* @filename: write to this file
|
||||||
|
*
|
||||||
|
* Create a stream attached to a file.
|
||||||
|
*
|
||||||
|
* See also: vips_stream_output_write().
|
||||||
|
*
|
||||||
|
* Returns: a new #VipsStream
|
||||||
|
*/
|
||||||
|
VipsStreamOutput *
|
||||||
|
vips_stream_output_new_from_filename( const char *filename )
|
||||||
|
{
|
||||||
|
VipsStreamOutput *stream;
|
||||||
|
|
||||||
|
VIPS_DEBUG_MSG( "vips_stream_output_new_from_filename: %s\n",
|
||||||
|
filename );
|
||||||
|
|
||||||
|
stream = VIPS_STREAM_OUTPUT(
|
||||||
|
g_object_new( VIPS_TYPE_STREAM_OUTPUT,
|
||||||
|
"filename", filename,
|
||||||
|
NULL ) );
|
||||||
|
|
||||||
|
if( vips_object_build( VIPS_OBJECT( stream ) ) ) {
|
||||||
|
VIPS_UNREF( stream );
|
||||||
|
return( NULL );
|
||||||
|
}
|
||||||
|
|
||||||
|
return( stream );
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
vips_stream_output_detach( VipsStreamOutput *stream )
|
||||||
|
{
|
||||||
|
VIPS_DEBUG_MSG( "vips_stream_output_detach:\n" );
|
||||||
|
|
||||||
|
g_assert( VIPS_STREAM( stream )->attached );
|
||||||
|
VIPS_STREAM( stream )->attached = FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
vips_stream_output_write( VipsStreamOutput *stream,
|
||||||
|
const unsigned char *buffer, size_t buffer_size )
|
||||||
|
{
|
||||||
|
VipsStreamOutputClass *class = VIPS_STREAM_OUTPUT_GET_CLASS( stream );
|
||||||
|
|
||||||
|
while( buffer_size > 0 ) {
|
||||||
|
ssize_t len;
|
||||||
|
|
||||||
|
if( class->write )
|
||||||
|
len = class->write( stream, buffer, buffer_size );
|
||||||
|
else
|
||||||
|
len = write( VIPS_STREAM( stream )->descriptor,
|
||||||
|
buffer, buffer_size );
|
||||||
|
|
||||||
|
#ifdef VIPS_DEBUG
|
||||||
|
if( len > 0 )
|
||||||
|
VIPS_DEBUG_MSG( "vips_stream_output_write: "
|
||||||
|
"written %zd bytes\n", len );
|
||||||
|
#endif /*VIPS_DEBUG*/
|
||||||
|
|
||||||
|
/* len == 0 isn't strictly an error, but we treat it as one to
|
||||||
|
* make sure we don't get stuck in this loop.
|
||||||
|
*/
|
||||||
|
if( len <= 0 ) {
|
||||||
|
vips_error_system( errno,
|
||||||
|
VIPS_OBJECT( stream )->nickname,
|
||||||
|
"%s", _( "write error" ) );
|
||||||
|
return( -1 );
|
||||||
|
}
|
||||||
|
|
||||||
|
buffer_size -= len;
|
||||||
|
buffer += len;
|
||||||
|
}
|
||||||
|
|
||||||
|
return( 0 );
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user