From 28daeb1a8a5f2ead77765769c5ce51289b20f0bb Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Tue, 15 Oct 2019 10:09:24 +0100 Subject: [PATCH] start adding mmap input to stream --- libvips/include/vips/internal.h | 1 + libvips/include/vips/stream.h | 10 ++++++ libvips/iofuncs/mapfile.c | 53 +++++++++++++++++++++++++++++++ libvips/iofuncs/stream.c | 55 ++++++++++++++++++++++++++++++++- 4 files changed, 118 insertions(+), 1 deletion(-) diff --git a/libvips/include/vips/internal.h b/libvips/include/vips/internal.h index 3ecd3f1c..37e7ff60 100644 --- a/libvips/include/vips/internal.h +++ b/libvips/include/vips/internal.h @@ -136,6 +136,7 @@ void *vips__link_map( VipsImage *image, gboolean upstream, char *vips__b64_encode( const unsigned char *data, size_t data_length ); unsigned char *vips__b64_decode( const char *buffer, size_t *data_length ); +gboolean vips__mmap_supported( int fd ); void *vips__mmap( int fd, int writeable, size_t length, gint64 offset ); int vips__munmap( const void *start, size_t length ); int vips_mapfile( VipsImage * ); diff --git a/libvips/include/vips/stream.h b/libvips/include/vips/stream.h index d81ea575..8cb2d911 100644 --- a/libvips/include/vips/stream.h +++ b/libvips/include/vips/stream.h @@ -129,6 +129,11 @@ typedef struct _VipsStreamInput { */ gboolean seekable; + /* TRUE is this descriptor supports mmap(). If not, then we have to + * read() the whole thing. + */ + gboolean mapable; + /*< private >*/ /* The current read point. @@ -148,6 +153,11 @@ typedef struct _VipsStreamInput { */ VipsBlob *blob; + /* If we've mmaped the file, the base and length. + */ + const void *baseaddr; + size_t length; + } VipsStreamInput; typedef struct _VipsStreamInputClass { diff --git a/libvips/iofuncs/mapfile.c b/libvips/iofuncs/mapfile.c index 33911aac..9b4b74d7 100644 --- a/libvips/iofuncs/mapfile.c +++ b/libvips/iofuncs/mapfile.c @@ -90,6 +90,59 @@ #include #endif /*OS_WIN32*/ +/* Does this fd support mmap. Pipes won't, for example. + */ +gboolean +vips__mmap_supported( int fd ) +{ + void *baseaddr; + size_t length = 4096; + off_t offset = 0; + +#ifdef OS_WIN32 +{ + HANDLE hFile = (HANDLE) _get_osfhandle( fd ); + + DWORD flProtect; + HANDLE hMMFile; + + DWORD dwDesiredAccess; + ULARGE_INTEGER quad; + DWORD dwFileOffsetHigh; + DWORD dwFileOffsetLow; + + flProtect = PAGE_READONLY; + if( !(hMMFile = CreateFileMapping( hFile, + NULL, flProtect, 0, 0, NULL )) ) + return( FALSE ); + + dwDesiredAccess = FILE_MAP_READ; + quad.QuadPart = offset; + dwFileOffsetLow = quad.LowPart; + dwFileOffsetHigh = quad.HighPart; + if( !(baseaddr = (char *)MapViewOfFile( hMMFile, dwDesiredAccess, + dwFileOffsetHigh, dwFileOffsetLow, length )) ) { + CloseHandle( hMMFile ); + return( FALSE ); + } + CloseHandle( hMMFile ); + UnmapViewOfFile( baseaddr ); +} +#else /*!OS_WIN32*/ +{ + int prot = PROT_READ; + int flags = MAP_SHARED; + + baseaddr = mmap( 0, length, prot, flags, fd, (off_t) offset ); + if( baseaddr == MAP_FAILED ) + return( FALSE ); + munmap( baseaddr, length ); +} +#endif /*OS_WIN32*/ + + return( TRUE ); +} + void * vips__mmap( int fd, int writeable, size_t length, gint64 offset ) { diff --git a/libvips/iofuncs/stream.c b/libvips/iofuncs/stream.c index f8c008fe..8f2f5066 100644 --- a/libvips/iofuncs/stream.c +++ b/libvips/iofuncs/stream.c @@ -63,6 +63,7 @@ #include #include +#include #include /* Try to make an O_BINARY ... sometimes need the leading '_'. @@ -270,6 +271,9 @@ vips_stream_input_build( VipsObject *object ) */ if( lseek( stream->descriptor, 0, SEEK_CUR ) != -1 ) input->seekable = TRUE; + + if( vips__mmap_supported( stream->descriptor ) ) + input->mapable = TRUE; } if( vips_object_argument_isset( object, "blob" ) ) @@ -396,6 +400,7 @@ vips_stream_input_class_init( VipsStreamInputClass *class ) static void vips_stream_input_init( VipsStreamInput *input ) { + input->length = -1; } /** @@ -606,9 +611,57 @@ vips_stream_input_read( VipsStreamInput *input, return( bytes_read ); } -unsigned char * +static const void * +vips_stream_input_try_map( VipsStreamInput *input, size_t *length ) +{ + VipsStream *stream = VIPS_STREAM( input ); + + if( input->length < 0 ) + input->length = vips_file_length( stream->descriptor ); + if( input->length < 0 ) + return( NULL ); + + if( !input->baseaddr ) { + input->baseaddr = vips__mmap( stream->descriptor, + FALSE, input->length, 0 ); + if( !input->baseaddr ) + return( NULL ); + } + + if( length ) + *length = input->length; + + return( input->baseaddr ); +} + +const void * vips_stream_input_map( VipsStreamInput *input, size_t *length ) { + /* Memory source ... easy! + */ + if( input->blob ) + return( vips_blob_get( input->blob, length ) ); + + /* An input that supports mmap. + */ + if( input->mapable ) { + if( !input->baseaddr ) { + input->baseaddr = vips_stream_input_try_map( input, + &input->length ); + if( !input->baseaddr ) + return( NULL ); + } + + if( length ) + *length = input->length; + + return( input->baseaddr ); + } + + /* Have to read() the whole thing. + */ + + return( NULL ); } int