diff --git a/libvips/include/vips/stream.h b/libvips/include/vips/stream.h index f337832b..49302817 100644 --- a/libvips/include/vips/stream.h +++ b/libvips/include/vips/stream.h @@ -240,10 +240,14 @@ gint64 vips_streami_size( VipsStreami *streami ); * class is very convenient. */ typedef struct _VipsStreamib { - VipsStreami parent_object; + VipsObject parent_object; /*< private >*/ + /* The VipsStreami we wrap. + */ + VipsStreami *streami; + /* The +1 means there's always a \0 byte at the end. */ char input_buffer[VIPS_STREAMIB_BUFFER_SIZE + 1]; @@ -251,24 +255,18 @@ typedef struct _VipsStreamib { char *read_point; int bytes_remaining; - /* No seeks on buffered streams, so an EOF flag is easy. - */ - gboolean eof; - } VipsStreamib; typedef struct _VipsStreamibClass { - VipsStreamiClass parent_class; + VipsObjectClass parent_class; } VipsStreamibClass; GType vips_streamib_get_type( void ); -VipsStreamib *vips_streamib_new_from_descriptor( int descriptor ); -VipsStreamib *vips_streamib_new_from_filename( const char *filename ); -VipsStreamib *vips_streamib_new_from_blob( VipsBlob *blob ); -VipsStreamib *vips_streamib_new_from_memory( const void *data, size_t size ); -VipsStreamib *vips_streamib_new_from_options( const char *options ); +VipsStreamib *vips_streamib_new( VipsStreami *streami ); + +void vips_streamib_unbuffer( VipsStreamib *streamib ); int vips_streamib_getc( VipsStreamib *streamib ); void vips_streamib_ungetc( VipsStreamib *streamib ); @@ -276,7 +274,8 @@ void vips_streamib_ungetc( VipsStreamib *streamib ); #define VIPS_STREAMIB_GETC( S ) ( \ (S)->bytes_remaining > 0 ? \ ((S)->bytes_remaining--, ((S)->read_point++)[0]) : \ - vips_streamib_getc( S )) + vips_streamib_getc( S ) \ +) int vips_streamib_get_line( VipsStreamib *streamib, const char **line ); int vips_streamib_get_line_copy( VipsStreamib *streamib, char **line ); diff --git a/libvips/iofuncs/Makefile.am b/libvips/iofuncs/Makefile.am index a8ad9ee6..6ac7db50 100644 --- a/libvips/iofuncs/Makefile.am +++ b/libvips/iofuncs/Makefile.am @@ -3,6 +3,7 @@ noinst_LTLIBRARIES = libiofuncs.la libiofuncs_la_SOURCES = \ stream.c \ streami.c \ + streamib.c \ streamo.c \ dbuf.c \ reorder.c \ diff --git a/libvips/iofuncs/stream.c b/libvips/iofuncs/stream.c index f048be6d..2091f405 100644 --- a/libvips/iofuncs/stream.c +++ b/libvips/iofuncs/stream.c @@ -125,8 +125,8 @@ vips_stream_class_init( VipsStreamClass *class ) G_STRUCT_OFFSET( VipsStream, descriptor ), -1, 1000000000, 0 ); - VIPS_ARG_STRING( class, "filename", 2, - _( "Filename" ), + VIPS_ARG_STRING( class, "filename", 2, + _( "Filename" ), _( "Name of file to open" ), VIPS_ARGUMENT_OPTIONAL_INPUT, G_STRUCT_OFFSET( VipsStream, filename ), diff --git a/libvips/iofuncs/streami.c b/libvips/iofuncs/streami.c index 8eeb4c88..0f99fc87 100644 --- a/libvips/iofuncs/streami.c +++ b/libvips/iofuncs/streami.c @@ -1043,214 +1043,3 @@ vips_streami_sniff( VipsStreami *streami, size_t length ) return( streami->sniff->data ); } - -G_DEFINE_TYPE( VipsStreamib, vips_streamib, VIPS_TYPE_STREAMI ); - -static void -vips_streamib_class_init( VipsStreamibClass *class ) -{ - VipsObjectClass *object_class = VIPS_OBJECT_CLASS( class ); - - object_class->nickname = "streamib"; - object_class->description = _( "buffered input stream" ); -} - -static void -vips_streamib_init( VipsStreamib *streamib ) -{ - streamib->read_point = streamib->input_buffer; - streamib->bytes_remaining = 0; - streamib->read_point[streamib->bytes_remaining] = '\0'; -} - -/* Returns -1 on error, 0 on EOF, otherwise bytes read. - */ -static ssize_t -vips_streamib_refill( VipsStreamib *streamib ) -{ - ssize_t bytes_read; - - /* We should not discard any unread bytes. - */ - g_assert( streamib->bytes_remaining == 0 ); - - bytes_read = vips_streami_read( VIPS_STREAMI( streamib ), - streamib->input_buffer, VIPS_STREAMIB_BUFFER_SIZE ); - if( bytes_read == -1 ) - return( -1 ); - - streamib->read_point = streamib->input_buffer; - streamib->bytes_remaining = bytes_read; - - /* Always add a null byte so we can use strchr() etc. on lines. This is - * safe because input_buffer is VIPS_STREAMIB_BUFFER_SIZE + 1 bytes. - */ - streamib->read_point[bytes_read] = '\0'; - - return( bytes_read ); -} - -/** - * vips_streamib_getc: - * @streamib: stream to operate on - * - * Fetch the next character from the stream. - * - * Use VIPS_STREAMIB_GETC() for speed. - * - * Returns: the next char from @streamib, -1 on read error or EOF. - */ -int -vips_streamib_getc( VipsStreamib *streamib ) -{ - int ch; - - if( streamib->bytes_remaining == 0 && - vips_streamib_refill( streamib ) <= 0 ) - return( -1 ); - - ch = streamib->read_point[0]; - - streamib->read_point += 1; - streamib->bytes_remaining -= 1; - - return( ch ); -} - -/** - * vips_streamib_ungetc: - * @streamib: stream to operate on - * - * The opposite of vips_streamib_getc: undo the previous getc. - * - * unget more than one character is undefined. Unget at the start of the file - * does nothing. - */ -void -vips_streamib_ungetc( VipsStreamib *streamib ) -{ - if( streamib->read_point > streamib->input_buffer ) { - streamib->read_point -= 1; - streamib->bytes_remaining += 1; - } -} - -/** - * vips_streamib_get_line: - * @streamib: stream to operate on - * @line: return the next line of text here - * - * Fetch the next line of text from @streamib and return in @line. The end of - * line character (or characters, for DOS files) are removed, and @line - * is terminated with a null (`\0` character). - * - * @line is set to NULL on end of file. - * - * If the line is longer than some arbitrary (but large) limit, is is - * truncated. If you need to be able to read very long lines, use the - * slower vips_streamib_get_line_copy(). - * - * The return value of @line is owned by @streamib and must not be freed. It - * is valid until the next call to vips_streamib_get_line(). - * - * Returns: 0 on success, -1 on error. - */ -int -vips_streamib_get_line( VipsStreamib *streamib, const char **line ) -{ - char *write_point; - int space_remaining; - int ch; - - write_point = streamib->line; - space_remaining = VIPS_STREAMIB_BUFFER_SIZE; - - while( (ch = VIPS_STREAMIB_GETC( streamib )) != -1 && - ch != '\n' && - space_remaining > 0 ) { - write_point[0] = ch; - write_point += 1; - space_remaining -= 1; - } - write_point[0] = '\0'; - - /* If we hit EOF immediately, return EOF. - */ - if( write_point == streamib->line ) { - *line = NULL; - return( 0 ); - } - - /* If we filled the output line without seeing \n, keep going to the - * next \n. - */ - if( ch != '\n' && - space_remaining == 0 ) { - while( (ch = VIPS_STREAMIB_GETC( streamib )) != -1 && - ch != '\n' ) - ; - } - - /* If we stopped on \n, try to skip any \r too. - */ - if( ch == '\n' ) { - if( VIPS_STREAMIB_GETC( streamib ) != '\r' ) - vips_streamib_ungetc( streamib ); - } - - *line = streamib->line; - - return( 0 ); -} - -/** - * vips_streamib_get_line_copy: - * @streamib: stream to operate on - * - * Return the next line of text from the stream. The newline character (or - * characters) are removed, and and the string is terminated with a null - * (`\0` character). - * - * The return result must be freed with g_free(). - * - * This is slower than vips_streamib_get_line(), but can work with lines of - * any length. - * - * Returns: the next line from the file, or NULL on EOF. - * - */ -int -vips_streamib_get_line_copy( VipsStreamib *streamib, char **line ) -{ - const unsigned char null = '\0'; - GByteArray *buffer; - int ch; - - buffer = g_byte_array_new(); - - while( (ch = VIPS_STREAMIB_GETC( streamib )) != -1 && - ch != '\n' ) { - unsigned char c = ch; - - g_byte_array_append( buffer, &c, 1 ); - } - - if( ch == -1 ) { - VIPS_FREEF( g_byte_array_unref, buffer ); - return( -1 ); - } - - g_byte_array_append( buffer, &null, 1 ); - - /* If we stopped on \n, try to skip any \r too. - */ - if( ch == '\n' ) { - if( VIPS_STREAMIB_GETC( streamib ) != '\r' ) - vips_streamib_ungetc( streamib ); - } - - *line = (char *) g_byte_array_free( buffer, FALSE ); - - return( 0 ); -} - diff --git a/libvips/iofuncs/streamib.c b/libvips/iofuncs/streamib.c new file mode 100644 index 00000000..c6276e97 --- /dev/null +++ b/libvips/iofuncs/streamib.c @@ -0,0 +1,338 @@ +/* A layer over streami to provide buffered and line-based input. + * + * 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 + + */ + +/* TODO + * + * - make something to parse input and implement rad load + * - gaussblur is missing the vector path again argh + * - can we map and then close the fd? how about on Windows? + * - make a subclass that lets you set vfuncs as params, inc. close(), + * is_pipe etc. + */ + +/* +#define VIPS_DEBUG + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#ifdef HAVE_UNISTD_H +#include +#endif /*HAVE_UNISTD_H*/ +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +G_DEFINE_TYPE( VipsStreamib, vips_streamib, VIPS_TYPE_OBJECT ); + +static void +vips_streamib_class_init( VipsStreamibClass *class ) +{ + VipsObjectClass *object_class = VIPS_OBJECT_CLASS( class ); + GObjectClass *gobject_class = G_OBJECT_CLASS( class ); + + gobject_class->set_property = vips_object_set_property; + gobject_class->get_property = vips_object_get_property; + + object_class->nickname = "streamib"; + object_class->description = _( "buffered input stream" ); + + VIPS_ARG_OBJECT( class, "input", 1, + _( "Input" ), + _( "Stream to load from" ), + VIPS_ARGUMENT_REQUIRED_INPUT, + G_STRUCT_OFFSET( VipsStreamib, streami ), + VIPS_TYPE_STREAMI ); + +} + +static void +vips_streamib_init( VipsStreamib *streamib ) +{ + streamib->read_point = streamib->input_buffer; + streamib->bytes_remaining = 0; + streamib->read_point[streamib->bytes_remaining] = '\0'; +} + +/** + * vips_streamib_new: + * @streami: stream to operate on + * + * Create a streamib wrapping a streami. + * + * Returns: a new #VipsStreamib + */ +VipsStreamib * +vips_streamib_new( VipsStreami *streami ) +{ + VipsStreamib *streamib; + + streamib = VIPS_STREAMIB( g_object_new( VIPS_TYPE_STREAMIB, + "input", streami, + NULL ) ); + + if( vips_object_build( VIPS_OBJECT( streamib ) ) ) { + VIPS_UNREF( streamib ); + return( NULL ); + } + + return( streamib ); +} + +/** + * vips_streamib_unbuffer: + * @streamib: stream to operate on + * + * Discard the input buffer and reset the read point. You must call this + * before using read or seek on the underlying #VipsStreami class. + */ +void +vips_streamib_unbuffer( VipsStreamib *streamib ) +{ + int bytes_in_buffer = streamib->read_point - streamib->input_buffer; + + streamib->read_point = streamib->input_buffer; + streamib->bytes_remaining = 0; + vips_streami_seek( streamib->streami, + -bytes_in_buffer, SEEK_SET ); +} + +/* Returns -1 on error, 0 on EOF, otherwise bytes read. + */ +static ssize_t +vips_streamib_refill( VipsStreamib *streamib ) +{ + ssize_t bytes_read; + + /* We should not discard any unread bytes. + */ + g_assert( streamib->bytes_remaining == 0 ); + + bytes_read = vips_streami_read( streamib->streami, + streamib->input_buffer, VIPS_STREAMIB_BUFFER_SIZE ); + if( bytes_read == -1 ) + return( -1 ); + + streamib->read_point = streamib->input_buffer; + streamib->bytes_remaining = bytes_read; + + /* Always add a null byte so we can use strchr() etc. on lines. This is + * safe because input_buffer is VIPS_STREAMIB_BUFFER_SIZE + 1 bytes. + */ + streamib->read_point[bytes_read] = '\0'; + + return( bytes_read ); +} + +/** + * vips_streamib_getc: + * @streamib: stream to operate on + * + * Fetch the next character from the stream. + * + * Use the macro VIPS_STREAMIB_GETC() instead for speed. + * + * Returns: the next char from @streamib, -1 on read error or EOF. + */ +int +vips_streamib_getc( VipsStreamib *streamib ) +{ + int ch; + + if( streamib->bytes_remaining == 0 && + vips_streamib_refill( streamib ) <= 0 ) + return( -1 ); + + ch = streamib->read_point[0]; + + streamib->read_point += 1; + streamib->bytes_remaining -= 1; + + return( ch ); +} + +/** VIPS_STREAMIB_GETC: + * @streamib: stream to operate on + * + * Fetch the next character from the stream. + * + * Returns: the next char from @streamib, -1 on read error or EOF. + */ + +/** + * vips_streamib_ungetc: + * @streamib: stream to operate on + * + * The opposite of vips_streamib_getc(): undo the previous getc. + * + * unget more than one character is undefined. Unget at the start of the file + * does nothing. + */ +void +vips_streamib_ungetc( VipsStreamib *streamib ) +{ + if( streamib->read_point > streamib->input_buffer ) { + streamib->read_point -= 1; + streamib->bytes_remaining += 1; + } +} + +/** + * vips_streamib_get_line: + * @streamib: stream to operate on + * @line: return the next line of text here + * + * Fetch the next line of text from @streamib and return in @line. The end of + * line character (or characters, for DOS files) are removed, and @line + * is terminated with a null (`\0` character). + * + * @line is set to NULL on end of file. + * + * If the line is longer than some arbitrary (but large) limit, is is + * truncated. If you need to be able to read very long lines, use the + * slower vips_streamib_get_line_copy(). + * + * The return value of @line is owned by @streamib and must not be freed. It + * is valid until the next call to vips_streamib_get_line(). + * + * Returns: 0 on success, -1 on error. + */ +int +vips_streamib_get_line( VipsStreamib *streamib, const char **line ) +{ + char *write_point; + int space_remaining; + int ch; + + write_point = streamib->line; + space_remaining = VIPS_STREAMIB_BUFFER_SIZE; + + while( (ch = VIPS_STREAMIB_GETC( streamib )) != -1 && + ch != '\n' && + space_remaining > 0 ) { + write_point[0] = ch; + write_point += 1; + space_remaining -= 1; + } + write_point[0] = '\0'; + + /* If we hit EOF immediately, return EOF. + */ + if( write_point == streamib->line ) { + *line = NULL; + return( 0 ); + } + + /* If we filled the output line without seeing \n, keep going to the + * next \n. + */ + if( ch != '\n' && + space_remaining == 0 ) { + while( (ch = VIPS_STREAMIB_GETC( streamib )) != -1 && + ch != '\n' ) + ; + } + + /* If we stopped on \n, try to skip any \r too. + */ + if( ch == '\n' ) { + if( VIPS_STREAMIB_GETC( streamib ) != '\r' ) + vips_streamib_ungetc( streamib ); + } + + *line = streamib->line; + + return( 0 ); +} + +/** + * vips_streamib_get_line_copy: + * @streamib: stream to operate on + * + * Return the next line of text from the stream. The newline character (or + * characters) are removed, and and the string is terminated with a null + * (`\0` character). + * + * The return result must be freed with g_free(). + * + * This is slower than vips_streamib_get_line(), but can work with lines of + * any length. + * + * Returns: the next line from the file, or NULL on EOF. + * + */ +int +vips_streamib_get_line_copy( VipsStreamib *streamib, char **line ) +{ + const unsigned char null = '\0'; + GByteArray *buffer; + int ch; + + buffer = g_byte_array_new(); + + while( (ch = VIPS_STREAMIB_GETC( streamib )) != -1 && + ch != '\n' ) { + unsigned char c = ch; + + g_byte_array_append( buffer, &c, 1 ); + } + + if( ch == -1 ) { + VIPS_FREEF( g_byte_array_unref, buffer ); + return( -1 ); + } + + g_byte_array_append( buffer, &null, 1 ); + + /* If we stopped on \n, try to skip any \r too. + */ + if( ch == '\n' ) { + if( VIPS_STREAMIB_GETC( streamib ) != '\r' ) + vips_streamib_ungetc( streamib ); + } + + *line = (char *) g_byte_array_free( buffer, FALSE ); + + return( 0 ); +}