add formatted output streams

you can printf() to output streams, implemented radiance stream write as
a test
This commit is contained in:
John Cupitt 2019-10-29 17:43:51 +00:00
parent 40d679f299
commit 23663e8a88
9 changed files with 395 additions and 264 deletions

View File

@ -1980,6 +1980,7 @@ vips_foreign_operation_init( void )
extern GType vips_foreign_load_rad_get_type( void );
extern GType vips_foreign_save_rad_file_get_type( void );
extern GType vips_foreign_save_rad_buffer_get_type( void );
extern GType vips_foreign_save_rad_stream_get_type( void );
extern GType vips_foreign_load_mat_get_type( void );
extern GType vips_foreign_load_ppm_get_type( void );
extern GType vips_foreign_save_ppm_get_type( void );
@ -2072,6 +2073,7 @@ vips_foreign_operation_init( void )
vips_foreign_load_rad_get_type();
vips_foreign_save_rad_file_get_type();
vips_foreign_save_rad_buffer_get_type();
vips_foreign_save_rad_stream_get_type();
#endif /*HAVE_RADIANCE*/
#ifdef HAVE_POPPLER

View File

@ -156,8 +156,7 @@ int vips__rad_israd( const char *filename );
int vips__rad_header( const char *filename, VipsImage *out );
int vips__rad_load( const char *filename, VipsImage *out );
int vips__rad_save( VipsImage *in, const char *filename );
int vips__rad_save_buf( VipsImage *in, void **obuf, size_t *olen );
int vips__rad_save( VipsImage *in, VipsStreamo *output );
extern const char *vips__rad_suffs[];

View File

@ -499,18 +499,6 @@ formatval( /* get format value (return true if format) */
}
static void
fputformat( /* put out a format value */
const char *s,
FILE *fp
)
{
fputs(FMTSTR, fp);
fputs(s, fp);
putc('\n', fp);
}
static int
getheader( /* get header from file */
FILE *fp,
@ -923,31 +911,6 @@ rle_scanline_write( COLR *scanline, int width,
}
}
/* Write a single scanline. buffer is at least MAX_LINE bytes and is used to
* construct the RLE scanline. Don't allocate this on the stack so we don't
* die too horribly on small-stack libc.
*/
static int
scanline_write( unsigned char *buffer, COLR *scanline, int width, FILE *fp )
{
if( width < MINELEN ||
width > MAXELEN )
/* Write as a flat scanline.
*/
return( fwrite( scanline, sizeof( COLR ), width, fp ) - width );
else {
/* An RLE scanline.
*/
int length;
rle_scanline_write( scanline, width, buffer, &length );
g_assert( length <= MAX_LINE );
return( fwrite( buffer, 1, length, fp ) - length );
}
}
/* What we track during radiance file read.
*/
typedef struct {
@ -1211,11 +1174,7 @@ vips__rad_load( const char *filename, VipsImage *out )
*/
typedef struct {
VipsImage *in;
char *filename;
FILE *fout;
VipsDbuf dbuf;
VipsStreamo *output;
char format[256];
double expos;
@ -1223,20 +1182,20 @@ typedef struct {
double aspect;
RGBPRIMS prims;
RESOLU rs;
unsigned char *line;
} Write;
static void
write_destroy( Write *write )
{
VIPS_FREE( write->filename );
VIPS_FREEF( fclose, write->fout );
vips_dbuf_destroy( &write->dbuf );
VIPS_FREE( write->line );
VIPS_UNREF( write->output );
vips_free( write );
}
static Write *
write_new( VipsImage *in )
write_new( VipsImage *in, VipsStreamo *output )
{
Write *write;
int i;
@ -1245,11 +1204,8 @@ write_new( VipsImage *in )
return( NULL );
write->in = in;
write->filename = NULL;
write->fout = NULL;
vips_dbuf_init( &write->dbuf );
write->output = output;
g_object_ref( output );
strcpy( write->format, COLRFMT );
write->expos = 1.0;
@ -1265,6 +1221,11 @@ write_new( VipsImage *in )
write->prims[3][0] = CIE_x_w;
write->prims[3][1] = CIE_y_w;
if( !(write->line = VIPS_ARRAY( NULL, MAX_LINE, unsigned char )) ) {
write_destroy( write );
return( NULL );
}
return( write );
}
@ -1279,7 +1240,8 @@ vips2rad_make_header( Write *write )
vips_image_get_double( write->in, "rad-expos", &write->expos );
if( vips_image_get_typeof( write->in, "rad-aspect" ) )
vips_image_get_double( write->in, "rad-aspect", &write->aspect );
vips_image_get_double( write->in,
"rad-aspect", &write->aspect );
if( vips_image_get_typeof( write->in, "rad-format" ) &&
!vips_image_get_string( write->in, "rad-format", &str ) )
@ -1292,7 +1254,8 @@ vips2rad_make_header( Write *write )
for( i = 0; i < 3; i++ )
if( vips_image_get_typeof( write->in, colcor_name[i] ) &&
!vips_image_get_double( write->in, colcor_name[i], &d ) )
!vips_image_get_double( write->in,
colcor_name[i], &d ) )
write->colcor[i] = d;
for( i = 0; i < 4; i++ )
@ -1316,16 +1279,53 @@ vips2rad_put_header( Write *write )
{
vips2rad_make_header( write );
fprintf( write->fout, "#?RADIANCE\n" );
vips_streamo_writef( write->output, "#?RADIANCE\n" );
vips_streamo_writef( write->output, "%s%s\n", FMTSTR, write->format );
vips_streamo_writef( write->output, "%s%e\n", EXPOSSTR, write->expos );
vips_streamo_writef( write->output,
"%s %f %f %f\n", COLCORSTR,
write->colcor[RED], write->colcor[GRN], write->colcor[BLU] );
vips_streamo_writef( write->output,
"SOFTWARE=vips %s\n", vips_version_string() );
vips_streamo_writef( write->output,
"%s%f\n", ASPECTSTR, write->aspect );
vips_streamo_writef( write->output,
"%s %.4f %.4f %.4f %.4f %.4f %.4f %.4f %.4f\n",
PRIMARYSTR,
write->prims[RED][CIEX], write->prims[RED][CIEY],
write->prims[GRN][CIEX], write->prims[GRN][CIEY],
write->prims[BLU][CIEX], write->prims[BLU][CIEY],
write->prims[WHT][CIEX], write->prims[WHT][CIEY] );
vips_streamo_writef( write->output, "\n" );
vips_streamo_writef( write->output,
"%s", resolu2str( resolu_buf, &write->rs ) );
fputformat( write->format, write->fout );
fputexpos( write->expos, write->fout );
fputcolcor( write->colcor, write->fout );
fprintf( write->fout, "SOFTWARE=vips %s\n", vips_version_string() );
fputaspect( write->aspect, write->fout );
fputprims( write->prims, write->fout );
fputs( "\n", write->fout );
fputsresolu( &write->rs, write->fout );
return( 0 );
}
/* Write a single scanline to buffer.
*/
static int
scanline_write( Write *write, COLR *scanline, int width )
{
if( width < MINELEN ||
width > MAXELEN ) {
/* Too large or small for RLE ... do a simple write.
*/
if( vips_streamo_write( write->output,
scanline, sizeof( COLR ) * width ) )
return( -1 );
}
else {
int length;
/* An RLE scanline.
*/
rle_scanline_write( scanline, width, write->line, &length );
if( vips_streamo_write( write->output, write->line, length ) )
return( -1 );
}
return( 0 );
}
@ -1334,23 +1334,12 @@ static int
vips2rad_put_data_block( VipsRegion *region, VipsRect *area, void *a )
{
Write *write = (Write *) a;
size_t size;
unsigned char *buffer;
int i;
/* You have to seek back after a write.
*/
buffer = vips_dbuf_get_write( &write->dbuf, &size );
vips_dbuf_seek( &write->dbuf, 0, SEEK_SET );
g_assert( size >= MAX_LINE );
for( i = 0; i < area->height; i++ ) {
VipsPel *p = VIPS_REGION_ADDR( region, 0, area->top + i );
if( scanline_write( buffer,
(COLR *) p, area->width, write->fout ) )
if( scanline_write( write, (COLR *) p, area->width ) )
return( -1 );
}
@ -1367,124 +1356,7 @@ vips2rad_put_data( Write *write )
}
int
vips__rad_save( VipsImage *in, const char *filename )
{
Write *write;
#ifdef DEBUG
printf( "vips2rad: writing \"%s\"\n", filename );
#endif /*DEBUG*/
if( vips_image_pio_input( in ) ||
vips_check_coding_rad( "vips2rad", in ) )
return( -1 );
if( !(write = write_new( in )) )
return( -1 );
write->filename = vips_strdup( NULL, filename );
write->fout = vips__file_open_write( filename, FALSE );
/* scanline_write() needs a buffer to write compressed scanlines to.
* We use the dbuf ... why not.
*/
vips_dbuf_allocate( &write->dbuf, MAX_LINE );
if( !write->filename ||
!write->fout ||
vips2rad_put_header( write ) ||
vips2rad_put_data( write ) ) {
write_destroy( write );
return( -1 );
}
write_destroy( write );
return( 0 );
}
static int
vips2rad_put_header_buf( Write *write )
{
vips2rad_make_header( write );
vips_dbuf_writef( &write->dbuf, "#?RADIANCE\n" );
vips_dbuf_writef( &write->dbuf, "%s%s\n", FMTSTR, write->format );
vips_dbuf_writef( &write->dbuf, "%s%e\n", EXPOSSTR, write->expos );
vips_dbuf_writef( &write->dbuf, "%s %f %f %f\n",
COLCORSTR,
write->colcor[RED], write->colcor[GRN], write->colcor[BLU] );
vips_dbuf_writef( &write->dbuf, "SOFTWARE=vips %s\n",
vips_version_string() );
vips_dbuf_writef( &write->dbuf, "%s%f\n", ASPECTSTR, write->aspect );
vips_dbuf_writef( &write->dbuf,
"%s %.4f %.4f %.4f %.4f %.4f %.4f %.4f %.4f\n",
PRIMARYSTR,
write->prims[RED][CIEX], write->prims[RED][CIEY],
write->prims[GRN][CIEX], write->prims[GRN][CIEY],
write->prims[BLU][CIEX], write->prims[BLU][CIEY],
write->prims[WHT][CIEX], write->prims[WHT][CIEY] );
vips_dbuf_writef( &write->dbuf, "\n" );
vips_dbuf_writef( &write->dbuf, "%s",
resolu2str( resolu_buf, &write->rs ) );
return( 0 );
}
/* Write a single scanline to buffer.
*/
static int
scanline_write_buf( Write *write, COLR *scanline, int width )
{
unsigned char *buffer;
size_t size;
int length;
vips_dbuf_allocate( &write->dbuf, MAX_LINE );
buffer = vips_dbuf_get_write( &write->dbuf, &size );
if( width < MINELEN ||
width > MAXELEN ) {
/* Write as a flat scanline.
*/
length = sizeof( COLR ) * width;
memcpy( buffer, scanline, length );
}
else
/* An RLE scanline.
*/
rle_scanline_write( scanline, width, buffer, &length );
vips_dbuf_seek( &write->dbuf, length - size, SEEK_CUR );
return( 0 );
}
static int
vips2rad_put_data_block_buf( VipsRegion *region, VipsRect *area, void *a )
{
Write *write = (Write *) a;
int i;
for( i = 0; i < area->height; i++ ) {
VipsPel *p = VIPS_REGION_ADDR( region, 0, area->top + i );
if( scanline_write_buf( write, (COLR *) p, area->width ) )
return( -1 );
}
return( 0 );
}
static int
vips2rad_put_data_buf( Write *write )
{
if( vips_sink_disc( write->in, vips2rad_put_data_block_buf, write ) )
return( -1 );
return( 0 );
}
int
vips__rad_save_buf( VipsImage *in, void **obuf, size_t *olen )
vips__rad_save( VipsImage *in, VipsStreamo *output )
{
Write *write;
@ -1495,16 +1367,16 @@ vips__rad_save_buf( VipsImage *in, void **obuf, size_t *olen )
if( vips_image_pio_input( in ) ||
vips_check_coding_rad( "vips2rad", in ) )
return( -1 );
if( !(write = write_new( in )) )
if( !(write = write_new( in, output )) )
return( -1 );
if( vips2rad_put_header_buf( write ) ||
vips2rad_put_data_buf( write ) ) {
if( vips2rad_put_header( write ) ||
vips2rad_put_data( write ) ) {
write_destroy( write );
return( -1 );
}
*obuf = vips_dbuf_steal( &write->dbuf, olen );
vips_streamo_finish( output );
write_destroy( write );

View File

@ -56,7 +56,6 @@
typedef struct _VipsForeignSaveRad {
VipsForeignSave parent_object;
char *filename;
} VipsForeignSaveRad;
typedef VipsForeignSaveClass VipsForeignSaveRadClass;
@ -85,14 +84,10 @@ static int vips_foreign_save_rad_format_table[10] = {
static void
vips_foreign_save_rad_class_init( VipsForeignSaveRadClass *class )
{
GObjectClass *gobject_class = G_OBJECT_CLASS( class );
VipsObjectClass *object_class = (VipsObjectClass *) class;
VipsForeignClass *foreign_class = (VipsForeignClass *) class;
VipsForeignSaveClass *save_class = (VipsForeignSaveClass *) class;
gobject_class->set_property = vips_object_set_property;
gobject_class->get_property = vips_object_get_property;
object_class->nickname = "radsave_base";
object_class->description = _( "save Radiance" );
@ -127,12 +122,19 @@ vips_foreign_save_rad_file_build( VipsObject *object )
VipsForeignSave *save = (VipsForeignSave *) object;
VipsForeignSaveRadFile *file = (VipsForeignSaveRadFile *) object;
VipsStreamo *output;
if( VIPS_OBJECT_CLASS( vips_foreign_save_rad_file_parent_class )->
build( object ) )
return( -1 );
if( vips__rad_save( save->ready, file->filename ) )
if( !(output = vips_streamo_new_from_filename( file->filename )) )
return( -1 );
if( vips__rad_save( save->ready, output ) ) {
VIPS_UNREF( output );
return( -1 );
}
VIPS_UNREF( output );
return( 0 );
}
@ -163,6 +165,60 @@ vips_foreign_save_rad_file_init( VipsForeignSaveRadFile *file )
{
}
typedef struct _VipsForeignSaveRadStream {
VipsForeignSaveRad parent_object;
VipsStreamo *output;
} VipsForeignSaveRadStream;
typedef VipsForeignSaveRadClass VipsForeignSaveRadStreamClass;
G_DEFINE_TYPE( VipsForeignSaveRadStream, vips_foreign_save_rad_stream,
vips_foreign_save_rad_get_type() );
static int
vips_foreign_save_rad_stream_build( VipsObject *object )
{
VipsForeignSave *save = (VipsForeignSave *) object;
VipsForeignSaveRadStream *stream = (VipsForeignSaveRadStream *) object;
if( VIPS_OBJECT_CLASS( vips_foreign_save_rad_stream_parent_class )->
build( object ) )
return( -1 );
if( vips__rad_save( save->ready, stream->output ) )
return( -1 );
return( 0 );
}
static void
vips_foreign_save_rad_stream_class_init( VipsForeignSaveRadStreamClass *class )
{
GObjectClass *gobject_class = G_OBJECT_CLASS( class );
VipsObjectClass *object_class = (VipsObjectClass *) class;
gobject_class->set_property = vips_object_set_property;
gobject_class->get_property = vips_object_get_property;
object_class->nickname = "radsave_stream";
object_class->description = _( "save image to Radiance stream" );
object_class->build = vips_foreign_save_rad_stream_build;
VIPS_ARG_OBJECT( class, "output", 1,
_( "Output" ),
_( "Stream to save to" ),
VIPS_ARGUMENT_REQUIRED_INPUT,
G_STRUCT_OFFSET( VipsForeignSaveRadStream, output ),
VIPS_TYPE_STREAMO );
}
static void
vips_foreign_save_rad_stream_init( VipsForeignSaveRadStream *stream )
{
}
typedef struct _VipsForeignSaveRadBuffer {
VipsForeignSaveRad parent_object;
@ -179,24 +235,27 @@ vips_foreign_save_rad_buffer_build( VipsObject *object )
{
VipsForeignSave *save = (VipsForeignSave *) object;
void *obuf;
size_t olen;
VipsStreamo *output;
VipsBlob *blob;
if( VIPS_OBJECT_CLASS( vips_foreign_save_rad_buffer_parent_class )->
build( object ) )
return( -1 );
if( vips__rad_save_buf( save->ready, &obuf, &olen ) )
if( !(output = vips_streamo_new_memory()) )
return( -1 );
/* vips__rad_save_buf() makes a buffer that needs g_free(), not
* vips_free().
*/
blob = vips_blob_new( (VipsCallbackFn) g_free, obuf, olen );
g_object_set( object, "buffer", blob, NULL );
if( vips__rad_save( save->ready, output ) ) {
VIPS_UNREF( output );
return( -1 );
}
g_object_get( output, "blob", &blob, NULL );
g_object_set( save, "buffer", blob, NULL );
vips_area_unref( VIPS_AREA( blob ) );
VIPS_UNREF( output );
return( 0 );
}
@ -299,3 +358,28 @@ vips_radsave_buffer( VipsImage *in, void **buf, size_t *len, ... )
return( result );
}
/**
* vips_radsave_stream: (method)
* @in: image to save
* @output: save image to this stream
* @...: %NULL-terminated list of optional named arguments
*
* As vips_radsave(), but save to a stream.
*
* See also: vips_radsave().
*
* Returns: 0 on success, -1 on error.
*/
int
vips_radsave_stream( VipsImage *in, VipsStreamo *output, ... )
{
va_list ap;
int result;
va_start( ap, output );
result = vips_call_split( "radsave_stream", ap, in, output );
va_end( ap );
return( result );
}

View File

@ -205,12 +205,9 @@ vips__tiff_openin_stream( VipsStreami *input )
printf( "vips__tiff_openin_stream:\n" );
#endif /*DEBUG*/
/* Unreffed on close(), see above.
*/
if( vips_streami_rewind( input ) )
return( NULL );
g_object_ref( input );
if( !(tiff = TIFFClientOpen( "stream input", "rm",
(thandle_t) input,
openin_stream_read,
@ -225,6 +222,10 @@ vips__tiff_openin_stream( VipsStreami *input )
return( NULL );
}
/* Unreffed on close(), see above.
*/
g_object_ref( input );
return( tiff );
}

View File

@ -68,7 +68,8 @@ size_t vips_dbuf_read( VipsDbuf *dbuf, unsigned char *data, size_t size );
unsigned char *vips_dbuf_get_write( VipsDbuf *dbuf, size_t *size );
gboolean vips_dbuf_write( VipsDbuf *dbuf,
const unsigned char *data, size_t size );
gboolean vips_dbuf_writef( VipsDbuf *dbuf, const char *fmt, ... );
gboolean vips_dbuf_writef( VipsDbuf *dbuf, const char *fmt, ... )
__attribute__((format(printf, 2, 3)));
gboolean vips_dbuf_write_amp( VipsDbuf *dbuf, const char *str );
void vips_dbuf_reset( VipsDbuf *dbuf );
void vips_dbuf_destroy( VipsDbuf *dbuf );

View File

@ -576,6 +576,8 @@ int vips_radsave( VipsImage *in, const char *filename, ... )
__attribute__((sentinel));
int vips_radsave_buffer( VipsImage *in, void **buf, size_t *len, ... )
__attribute__((sentinel));
int vips_radsave_stream( VipsImage *in, VipsStreamo *output, ... )
__attribute__((sentinel));
int vips_pdfload( const char *filename, VipsImage **out, ... )
__attribute__((sentinel));

View File

@ -184,13 +184,14 @@ typedef struct _VipsStreamiClass {
*/
/* Read from the stream into the supplied buffer, args exactly as
* read(2).
* read(2). Set errno on error.
*/
ssize_t (*read)( VipsStreami *, void *, size_t );
/* Seek to a certain position, args exactly as lseek(2).
/* Seek to a certain position, args exactly as lseek(2). Set errno on
* error.
*
* Unseekable streams should just return -1. VipsStreami will then
* Unseekable streams should always return -1. VipsStreami will then
* seek by _read()ing bytes into memory as required.
*/
gint64 (*seek)( VipsStreami *, gint64 offset, int );
@ -230,6 +231,8 @@ gint64 vips_streami_size( VipsStreami *streami );
(G_TYPE_INSTANCE_GET_CLASS( (obj), \
VIPS_TYPE_STREAMO, VipsStreamoClass ))
#define VIPS_STREAMO_BUFFER_SIZE (4096)
/* Output to something like a socket, pipe or memory area.
*/
typedef struct _VipsStreamo {
@ -245,16 +248,22 @@ typedef struct _VipsStreamo {
*/
VipsBlob *blob;
/* Buffer small writes here.
*/
unsigned char output_buffer[VIPS_STREAMO_BUFFER_SIZE];
unsigned char *write_point;
int bytes_remaining;
} VipsStreamo;
typedef struct _VipsStreamoClass {
VipsStreamClass parent_class;
/* If defined, output some bytes with this. Otherwise use write().
/* Write to output. Args exactly as write(2).
*/
ssize_t (*write)( VipsStreamo *, const void *, size_t );
/* A complete output image has been generated, so do any clearing up,
/* Output has been generated, so do any clearing up,
* eg. copy the bytes we saved in memory to the stream blob.
*/
void (*finish)( VipsStreamo * );
@ -269,6 +278,10 @@ VipsStreamo *vips_streamo_new_memory( void );
int vips_streamo_write( VipsStreamo *streamo, const void *data, size_t length );
void vips_streamo_finish( VipsStreamo *streamo );
int vips_streamo_writef( VipsStreamo *streamo, const char *fmt, ... )
__attribute__((format(printf, 2, 3)));
int vips_streamo_write_amp( VipsStreamo *streamo, const char *str );
#ifdef __cplusplus
}
#endif /*__cplusplus*/

View File

@ -34,9 +34,7 @@
/* TODO
*
* - filename encoding
* - need printf()-style output? eg. for CSV write ... see dbuf.c
* - make vfuncs just call write() and seek() (for tiff), all other processing
* in the invocation ... cf. streami
* - seem to have some test-suite ref leaks?
* - test we can really change all behaviour in the subclass ... add callbacks
* as well to make it simpler for language bindings
*/
@ -157,37 +155,15 @@ vips_streamo_write_real( VipsStreamo *streamo, const void *data, size_t length )
{
VipsStream *stream = VIPS_STREAM( streamo );
ssize_t len;
VIPS_DEBUG_MSG( "vips_streamo_write_real: %zd bytes\n", length );
if( streamo->memory ) {
g_byte_array_append( streamo->memory, data, length );
len = length;
}
else
len = write( stream->descriptor, data, length );
return( len );
return( write( stream->descriptor, data, length ) );
}
static void
vips_streamo_finish_real( VipsStreamo *streamo )
{
VIPS_DEBUG_MSG( "vips_streamo_finish_real:\n" );
/* Move the stream buffer into the blob so it can be read out.
*/
if( streamo->memory ) {
unsigned char *data;
size_t length;
length = streamo->memory->len;
data = g_byte_array_free( streamo->memory, FALSE );
streamo->memory = NULL;
vips_blob_set( streamo->blob,
(VipsCallbackFn) g_free, data, length );
}
}
static void
@ -224,6 +200,8 @@ static void
vips_streamo_init( VipsStreamo *streamo )
{
streamo->blob = vips_blob_new( NULL, NULL, 0 );
streamo->bytes_remaining = VIPS_STREAMO_BUFFER_SIZE;
streamo->write_point = streamo->output_buffer;
}
/**
@ -315,35 +293,107 @@ vips_streamo_new_memory( void )
return( streamo );
}
int
vips_streamo_write( VipsStreamo *streamo, const void *data, size_t length )
static int
vips_streamo_write_unbuffered( VipsStreamo *streamo,
const void *data, size_t length )
{
VipsStreamoClass *class = VIPS_STREAMO_GET_CLASS( streamo );
VIPS_DEBUG_MSG( "vips_streamo_write: %zd bytes\n", length );
if( streamo->memory )
g_byte_array_append( streamo->memory, data, length );
else
while( length > 0 ) {
ssize_t n;
while( length > 0 ) {
ssize_t n;
n = class->write( streamo, data, length );
n = class->write( streamo, data, length );
/* n == 0 isn't strictly an error, but we treat it as
* one to make sure we don't get stuck in this loop.
*/
if( n <= 0 ) {
vips_error_system( errno,
vips_stream_nick(
VIPS_STREAM( streamo ) ),
"%s", _( "write error" ) );
return( -1 );
}
/* n == 0 isn't strictly an error, but we treat it as one to
* make sure we don't get stuck in this loop.
*/
if( n <= 0 ) {
vips_error_system( errno,
vips_stream_nick( VIPS_STREAM( streamo ) ),
"%s", _( "write error" ) );
return( -1 );
length -= n;
data += n;
}
length -= n;
data += n;
return( 0 );
}
static int
vips_streamo_flush( VipsStreamo *streamo )
{
int bytes_in_buffer =
VIPS_STREAMO_BUFFER_SIZE - streamo->bytes_remaining;
g_assert( bytes_in_buffer >= 0 );
g_assert( bytes_in_buffer <= VIPS_STREAMO_BUFFER_SIZE );
if( bytes_in_buffer > 0 ) {
if( vips_streamo_write_unbuffered( streamo,
streamo->output_buffer, bytes_in_buffer ) )
return( -1 );
streamo->bytes_remaining = VIPS_STREAMO_BUFFER_SIZE;
streamo->write_point = streamo->output_buffer;
}
return( 0 );
}
/**
* vips_streamo_write:
* @streamo: output stream to operate on
* @buffer: bytes to write
* @length: length of @buffer in bytes
*
* Write @length bytes from @buffer to the output.
*
* Returns: 0 on success, -1 on error.
*/
int
vips_streamo_write( VipsStreamo *streamo, const void *buffer, size_t length )
{
VIPS_DEBUG_MSG( "vips_streamo_write: %zd bytes\n", length );
if( streamo->bytes_remaining >= length ) {
memcpy( streamo->write_point, buffer, length );
streamo->bytes_remaining -= length;
streamo->write_point += length;
}
else {
if( vips_streamo_flush( streamo ) )
return( -1 );
if( streamo->bytes_remaining >= length ) {
memcpy( streamo->write_point, buffer, length );
streamo->bytes_remaining -= length;
streamo->write_point += length;
}
else
/* The buffer is empty and we still have too much
* data. Write directly.
*/
if( vips_streamo_write_unbuffered( streamo,
buffer, length ) )
return( -1 );
}
return( 0 );
}
/**
* vips_streamo_finish:
* @streamo: output stream to operate on
* @buffer: bytes to write
* @length: length of @buffer in bytes
*
* Call this at the end of write to make the stream do any cleaning up.
*/
void
vips_streamo_finish( VipsStreamo *streamo )
{
@ -351,5 +401,112 @@ vips_streamo_finish( VipsStreamo *streamo )
VIPS_DEBUG_MSG( "vips_streamo_finish:\n" );
class->finish( streamo );
(void) vips_streamo_flush( streamo );
/* Move the stream buffer into the blob so it can be read out.
*/
if( streamo->memory ) {
unsigned char *data;
size_t length;
length = streamo->memory->len;
data = g_byte_array_free( streamo->memory, FALSE );
streamo->memory = NULL;
vips_blob_set( streamo->blob,
(VipsCallbackFn) g_free, data, length );
}
else
class->finish( streamo );
}
/**
* vips_streamo_writef:
* @streamo: output stream to operate on
* @fmt: <function>printf()</function>-style format string
* @...: arguments to format string
*
* Format the string and write to @streamo.
*
* Returns: 0 on success, and -1 on error.
*/
int
vips_streamo_writef( VipsStreamo *streamo, const char *fmt, ... )
{
va_list ap;
char *line;
int result;
va_start( ap, fmt );
line = g_strdup_vprintf( fmt, ap );
va_end( ap );
result = vips_streamo_write( streamo,
(unsigned char *) line, strlen( line ) );
g_free( line );
return( result );
}
/**
* vips_streamo_write_amp:
* @streamo: output stream to operate on
* @str: string to write
*
* Write @str to @streamo, but escape stuff that xml hates in text. Our
* argument string is utf-8.
*
* XML rules:
*
* - We must escape &<>
* - Don't escape \n, \t, \r
* - Do escape the other ASCII codes.
*
* Returns: 0 on success, -1 on error.
*/
int
vips_streamo_write_amp( VipsStreamo *streamo, const char *str )
{
const char *p;
for( p = str; *p; p++ )
if( *p < 32 &&
*p != '\n' &&
*p != '\t' &&
*p != '\r' ) {
/* You'd think we could output "&#x02%x;", but xml
* 1.0 parsers barf on that. xml 1.1 allows this, but
* there are almost no parsers.
*
* U+2400 onwards are unicode glyphs for the ASCII
* control characters, so we can use them -- thanks
* electroly.
*/
if( !vips_streamo_writef( streamo,
"&#x%04x;", 0x2400 + *p ) )
return( -1 );
}
else if( *p == '<' ) {
if( !vips_streamo_write( streamo,
(guchar *) "&lt;", 4 ) )
return( -1 );
}
else if( *p == '>' ) {
if( !vips_streamo_write( streamo,
(guchar *) "&gt;", 4 ) )
return( -1 );
}
else if( *p == '&' ) {
if( !vips_streamo_write( streamo,
(guchar *) "&amp;", 5 ) )
return( -1 );
}
else {
if( !vips_streamo_write( streamo,
(guchar *) p, 1 ) )
return( -1 );
}
return( 0 );
}