use expat for xml read

we were using libxml for xml load, use expat instead, we get it for free
with glib
This commit is contained in:
John Cupitt 2017-02-25 13:07:43 +00:00
parent dc2b567ee2
commit 134ce0560c
3 changed files with 182 additions and 184 deletions

View File

@ -34,6 +34,8 @@
- vips_region_shrink() knows about alpha, helps dzsave and tiffsave - vips_region_shrink() knows about alpha, helps dzsave and tiffsave
- vips_sequential() no longer stalls ahead threads, instead we constrain sinks - vips_sequential() no longer stalls ahead threads, instead we constrain sinks
more tightly ... this makes seq more versatile and more reliable more tightly ... this makes seq more versatile and more reliable
- use expat, not libxml, for XML load ... removes a required dependency, since
we get expat as part of glib
8/12/16 started 8.4.5 8/12/16 started 8.4.5
- allow libgsf-1.14.26 to help centos, thanks tdiprima - allow libgsf-1.14.26 to help centos, thanks tdiprima

View File

@ -77,14 +77,6 @@ extern "C" {
*/ */
#define VIPS_META_ICC_NAME "icc-profile-data" #define VIPS_META_ICC_NAME "icc-profile-data"
/**
* VIPS_META_XML:
*
* The original XML that was used to code the metadata after reading a VIPS
* format file.
*/
#define VIPS_META_XML "xml-header"
/** /**
* VIPS_META_IMAGEDESCRIPTION: * VIPS_META_IMAGEDESCRIPTION:
* *

View File

@ -17,6 +17,8 @@
* image.c ... this file now just does read / write to disc * image.c ... this file now just does read / write to disc
* 28/3/11 * 28/3/11
* - moved to vips_ namespace * - moved to vips_ namespace
* 25/2/17
* - use expat for xml read
*/ */
/* /*
@ -435,9 +437,9 @@ vips__read_extension_block( VipsImage *im, int *size )
psize = image_pixel_length( im ); psize = image_pixel_length( im );
g_assert( im->file_length > 0 ); g_assert( im->file_length > 0 );
if( im->file_length - psize > 10 * 1024 * 1024 ) { if( im->file_length - psize > 100 * 1024 * 1024 ) {
vips_error( "VipsImage", vips_error( "VipsImage",
"%s", _( "more than a 10 megabytes of XML? " "%s", _( "more than 100 megabytes of XML? "
"sufferin' succotash!" ) ); "sufferin' succotash!" ) );
return( NULL ); return( NULL );
} }
@ -457,74 +459,108 @@ vips__read_extension_block( VipsImage *im, int *size )
return( buf ); return( buf );
} }
/* Read everything after the pixels into memory. static int
parser_read_fd( XML_Parser parser, int fd )
FIXME ... why can't we use xmlParserInputBufferCreateFd and parse
directly from the fd rather than having to read the stupid thing into
memory
the libxml API docs are impossible to decipher
*/
static xmlDoc *
read_xml( VipsImage *im )
{ {
void *buf; const int chunk_size = 1024;
int size;
xmlDoc *doc;
xmlNode *node;
if( !(buf = vips__read_extension_block( im, &size )) ) ssize_t bytes_read;
return( NULL );
if( !(doc = xmlParseMemory( buf, size )) ) { do {
vips_free( buf ); void *buf;
return( NULL );
} if( !(buf = XML_GetBuffer( parser, chunk_size )) ) {
vips_free( buf ); vips_error( "VipsImage",
if( !(node = xmlDocGetRootElement( doc )) || "%s", _( "unable to allocate read buffer" ) );
!node->nsDef || return( -1 );
!vips_isprefix( NAMESPACE, (char *) node->nsDef->href ) ) { }
vips_error( "VipsImage", bytes_read = read( fd, buf, chunk_size );
"%s", _( "incorrect namespace in XML" ) ); if( bytes_read == (ssize_t) -1 ) {
xmlFreeDoc( doc ); vips_error( "VipsImage",
return( NULL ); "%s", _( "read error while fetching XML" ) );
} return( -1 );
}
if( !XML_ParseBuffer( parser, bytes_read, bytes_read == 0 ) ) {
vips_error( "VipsImage",
"%s", _( "XML parse error" ) );
return( -1 );
}
} while( bytes_read > 0 );
return( 0 );
}
#define MAX_PARSE_ATTR (256)
/* What we track during expat parse.
*/
typedef struct _VipsExpatParse {
VipsImage *image;
/* TRUE for in header section.
*/
gboolean header;
/* For the current node, the type and name.
*/
XML_Char type[MAX_PARSE_ATTR];
XML_Char name[MAX_PARSE_ATTR];
/* Accumulate data here.
*/
char *data;
size_t current_size;
gboolean error;
} VipsExpatParse;
static void
parser_element_start_handler( void *user_data,
const XML_Char *name, const XML_Char **atts )
{
VipsExpatParse *vep = (VipsExpatParse *) user_data;
const XML_Char **p;
#ifdef DEBUG #ifdef DEBUG
printf( "read_xml: namespace == %s\n", node->nsDef->href ); printf( "parser_element_start: %s\n", name );
for( p = atts; *p; p += 2 )
printf( "%s = %s\n", p[0], p[1] );
#endif /*DEBUG*/ #endif /*DEBUG*/
return( doc ); if( strcmp( name, "field" ) == 0 ) {
for( p = atts; *p; p += 2 ) {
if( strcmp( p[0], "name" ) == 0 )
vips_strncpy( vep->name, p[1], MAX_PARSE_ATTR );
if( strcmp( p[0], "type" ) == 0 )
vips_strncpy( vep->type, p[1], MAX_PARSE_ATTR );
}
vep->current_size = 0;
}
else if( strcmp( name, "header" ) == 0 )
vep->header = TRUE;
else if( strcmp( name, "meta" ) == 0 )
vep->header = FALSE;
else if( strcmp( name, "root" ) == 0 ) {
for( p = atts; *p; p += 2 )
if( strcmp( p[0], "xmlns" ) == 0 &&
!vips_isprefix( NAMESPACE, p[1] ) ) {
vips_error( "VipsImage", "%s",
_( "incorrect namespace in XML" ) );
vep->error = TRUE;
}
}
} }
/* Find the first child node with a name. static void
*/ parser_append( VipsExpatParse *vep, const XML_Char *data, int len )
static xmlNode *
get_node( xmlNode *base, const char *name )
{ {
xmlNode *i; size_t new_size = vep->current_size + len;
for( i = base->children; i; i = i->next ) vep->data = g_realloc( vep->data, new_size );
if( strcmp( (char *) i->name, name ) == 0 ) memcpy( vep->data + vep->current_size, data, len );
return( i ); vep->current_size = new_size;
return( NULL );
}
/* Read a string property to a buffer. TRUE for success.
*/
static int
get_sprop( xmlNode *xnode, const char *name, char *buf, int sz )
{
char *value = (char *) xmlGetProp( xnode, (xmlChar *) name );
if( !value )
return( 0 );
vips_strncpy( buf, value, sz );
VIPS_FREEF( xmlFree, value );
return( 1 );
} }
/* Chop history into lines, add each one as a refstring. /* Chop history into lines, add each one as a refstring.
@ -556,119 +592,76 @@ set_history( VipsImage *im, char *history )
im->history_list = g_slist_reverse( history_list ); im->history_list = g_slist_reverse( history_list );
} }
/* Load header fields.
*/
static int static int
rebuild_header_builtin( VipsImage *im, xmlNode *i ) set_meta( VipsImage *image, GType gtype, const char *name, const char *data )
{ {
char name[256]; GValue save_value = { 0 };
GValue value = { 0 };
if( get_sprop( i, "name", name, 256 ) ) { g_value_init( &save_value, VIPS_TYPE_SAVE_STRING );
if( strcmp( name, "Hist" ) == 0 ) { vips_value_set_save_string( &save_value, data );
char *history;
/* Have to take (another) copy, since we need to free g_value_init( &value, gtype );
* with xmlFree(). if( !g_value_transform( &save_value, &value ) ) {
g_value_unset( &save_value );
vips_error( "VipsImage", "%s",
_( "error transforming from save format" ) );
return( -1 );
}
vips_image_set( image, name, &value );
g_value_unset( &save_value );
g_value_unset( &value );
return( 0 );
}
static void
parser_element_end_handler( void *user_data, const XML_Char *name )
{
VipsExpatParse *vep = (VipsExpatParse *) user_data;
#ifdef DEBUG
printf( "parser_element_end_handler: %s\n", name );
#endif /*DEBUG*/
if( strcmp( name, "field" ) == 0 ) {
parser_append( vep, "", 1 );
#ifdef DEBUG
printf( "parser_element_end_handler: %zd bytes\n",
vep->current_size );
#endif /*DEBUG*/
if( vep->header ) {
if( strcmp( name, "Hist" ) == 0 )
set_history( vep->image, vep->data );
}
else {
GType gtype = g_type_from_name( vep->type );
/* Can we convert from VIPS_SAVE_STRING to type?
*/ */
history = (char *) xmlNodeGetContent( i ); if( gtype &&
set_history( im, history ); g_value_type_transformable(
xmlFree( history ); VIPS_TYPE_SAVE_STRING, gtype ) &&
set_meta( vep->image,
gtype, vep->name, vep->data ) )
vep->error = TRUE;
} }
} }
return( 0 );
} }
/* Load meta fields. static void
*/ parser_data_handler( void *user_data, const XML_Char *data, int len )
static int
rebuild_header_meta( VipsImage *im, xmlNode *i )
{ {
char name[256]; VipsExpatParse *vep = (VipsExpatParse *) user_data;
char type[256];
if( get_sprop( i, "name", name, 256 ) && #ifdef DEBUG
get_sprop( i, "type", type, 256 ) ) { printf( "parser_data_handler: %d bytes\n", len );
GType gtype = g_type_from_name( type ); #endif /*DEBUG*/
/* Can we convert from VIPS_SAVE_STRING to type? parser_append( vep, data, len );
*/
if( gtype &&
g_value_type_transformable(
VIPS_TYPE_SAVE_STRING, gtype ) ) {
char *content;
GValue save_value = { 0 };
GValue value = { 0 };
content = (char *) xmlNodeGetContent( i );
g_value_init( &save_value, VIPS_TYPE_SAVE_STRING );
vips_value_set_save_string( &save_value, content );
xmlFree( content );
g_value_init( &value, gtype );
if( !g_value_transform( &save_value, &value ) ) {
g_value_unset( &save_value );
vips_error( "VipsImage",
"%s", _( "error transforming from "
"save format" ) );
return( -1 );
}
vips_image_set( im, name, &value );
g_value_unset( &save_value );
g_value_unset( &value );
}
}
return( 0 );
}
static xmlDoc *
get_xml( VipsImage *im )
{
if( vips_image_get_typeof( im, VIPS_META_XML ) ) {
xmlDoc *doc;
if( vips_image_get_area( im, VIPS_META_XML, (void *) &doc ) )
return( NULL );
return( doc );
}
return( NULL );
}
/* Rebuild header fields that depend on stuff saved in xml.
*/
static int
rebuild_header( VipsImage *im )
{
xmlDoc *doc;
if( (doc = get_xml( im )) ) {
xmlNode *root;
xmlNode *block;
if( !(root = xmlDocGetRootElement( doc )) )
return( -1 );
if( (block = get_node( root, "header" )) ) {
xmlNode *i;
for( i = block->children; i; i = i->next )
if( strcmp( (char *) i->name, "field" ) == 0 )
if( rebuild_header_builtin( im, i ) )
return( -1 );
}
if( (block = get_node( root, "meta" )) ) {
xmlNode *i;
for( i = block->children; i; i = i->next )
if( strcmp( (char *) i->name, "field" ) == 0 )
if( rebuild_header_meta( im, i ) )
return( -1 );
}
}
return( 0 );
} }
/* Called at the end of vips open ... get any XML after the pixel data /* Called at the end of vips open ... get any XML after the pixel data
@ -677,24 +670,35 @@ rebuild_header( VipsImage *im )
static int static int
readhist( VipsImage *im ) readhist( VipsImage *im )
{ {
/* Junk any old xml meta. XML_Parser parser;
*/ VipsExpatParse vep;
if( vips_image_get_typeof( im, VIPS_META_XML ) )
vips_image_set_area( im, VIPS_META_XML, NULL, NULL );
if( vips__has_extension_block( im ) ) { if( vips__seek( im->fd, image_pixel_length( im ) ) )
xmlDoc *doc;
if( !(doc = read_xml( im )) )
return( -1 );
vips_image_set_area( im, VIPS_META_XML,
(VipsCallbackFn) xmlFreeDoc, doc );
}
if( rebuild_header( im ) )
return( -1 ); return( -1 );
return( 0 ); parser = XML_ParserCreate( "UTF-8" );
vep.image = im;
vep.data = NULL;
vep.current_size = 0;
vep.error = FALSE;
XML_SetUserData( parser, &vep );
XML_SetElementHandler( parser,
parser_element_start_handler, parser_element_end_handler );
XML_SetCharacterDataHandler( parser, parser_data_handler );
if( parser_read_fd( parser, im->fd ) ||
vep.error ) {
g_free( vep.data );
XML_ParserFree( parser );
return( -1 );
}
g_free( vep.data );
XML_ParserFree( parser );
return( 0 );
} }
#define MAX_STRSIZE (32768) /* Max size of text for stack strings */ #define MAX_STRSIZE (32768) /* Max size of text for stack strings */