448 lines
9.7 KiB
C
448 lines
9.7 KiB
C
/* A dynamic memory buffer that expands as you write.
|
|
*/
|
|
|
|
/*
|
|
|
|
Copyright (C) 1991-2003 The National Gallery
|
|
|
|
This library 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.1 of the License, or (at your option) any later version.
|
|
|
|
This library 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 library; 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 DEBUG
|
|
*/
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
#include <config.h>
|
|
#endif /*HAVE_CONFIG_H*/
|
|
#include <glib/gi18n-lib.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <vips/vips.h>
|
|
|
|
/**
|
|
* vips_dbuf_init:
|
|
* @dbuf: the buffer
|
|
*
|
|
* Initialize @dbuf.
|
|
*/
|
|
void
|
|
vips_dbuf_init( VipsDbuf *dbuf )
|
|
{
|
|
dbuf->data = NULL;
|
|
dbuf->allocated_size = 0;
|
|
dbuf->data_size = 0;
|
|
dbuf->write_point = 0;
|
|
}
|
|
|
|
/**
|
|
* vips_dbuf_minimum_size:
|
|
* @dbuf: the buffer
|
|
* @size: the minimum size
|
|
*
|
|
* Make sure @dbuf is at least @size bytes.
|
|
*
|
|
* Returns: %FALSE on out of memory, %TRUE otherwise.
|
|
*/
|
|
gboolean
|
|
vips_dbuf_minimum_size( VipsDbuf *dbuf, size_t size )
|
|
{
|
|
if( size > dbuf->allocated_size ) {
|
|
const size_t new_allocated_size = 3 * (16 + size) / 2;
|
|
|
|
unsigned char *new_data;
|
|
|
|
if( !(new_data =
|
|
g_try_realloc( dbuf->data, new_allocated_size )) ) {
|
|
vips_error( "VipsDbuf", "%s", _( "out of memory" ) );
|
|
return( FALSE );
|
|
}
|
|
|
|
dbuf->data = new_data;
|
|
dbuf->allocated_size = new_allocated_size;
|
|
}
|
|
|
|
return( TRUE );
|
|
}
|
|
|
|
/**
|
|
* vips_dbuf_allocate:
|
|
* @dbuf: the buffer
|
|
* @size: the size to allocate
|
|
*
|
|
* Make sure @dbuf has at least @size bytes available after the write point.
|
|
*
|
|
* Returns: %FALSE on out of memory, %TRUE otherwise.
|
|
*/
|
|
gboolean
|
|
vips_dbuf_allocate( VipsDbuf *dbuf, size_t size )
|
|
{
|
|
return( vips_dbuf_minimum_size( dbuf, dbuf->write_point + size ) );
|
|
}
|
|
|
|
/**
|
|
* vips_dbuf_read:
|
|
* @dbuf: the buffer
|
|
* @data: read to this area
|
|
* @size: read up to this many bytes
|
|
*
|
|
* Up to @size bytes are read from the buffer and copied to @data. The number
|
|
* of bytes transferred is returned.
|
|
*
|
|
* Returns: the number of bytes transferred.
|
|
*/
|
|
size_t
|
|
vips_dbuf_read( VipsDbuf *dbuf, unsigned char *data, size_t size )
|
|
{
|
|
const size_t available = dbuf->data_size - dbuf->write_point;
|
|
const size_t copied = VIPS_MIN( size, available );
|
|
|
|
memcpy( data, dbuf->data + dbuf->write_point, copied );
|
|
dbuf->write_point += copied;
|
|
|
|
return( copied );
|
|
}
|
|
|
|
/**
|
|
* vips_dbuf_get_write:
|
|
* @dbuf: the buffer
|
|
* @size: (allow-none): optionally return length in bytes here
|
|
*
|
|
* Return a pointer to an area you can write to, return length of area in
|
|
* @size. Use vips_dbuf_allocate() before this call to set a minimum amount of
|
|
* space to have available.
|
|
*
|
|
* The write point moves to just beyond the returned block. Use
|
|
* vips_dbuf_seek() to move it back again.
|
|
*
|
|
* Returns: (transfer none): start of write area.
|
|
*/
|
|
unsigned char *
|
|
vips_dbuf_get_write( VipsDbuf *dbuf, size_t *size )
|
|
{
|
|
unsigned char *write = dbuf->data + dbuf->write_point;
|
|
const size_t available = dbuf->allocated_size - dbuf->write_point;
|
|
|
|
memset( write, 0, available );
|
|
dbuf->write_point = dbuf->allocated_size;
|
|
dbuf->data_size = dbuf->allocated_size;
|
|
|
|
if( size )
|
|
*size = available;
|
|
|
|
return( write );
|
|
}
|
|
|
|
/**
|
|
* vips_dbuf_write:
|
|
* @dbuf: the buffer
|
|
* @data: the data to write to the buffer
|
|
* @size: the size of the len to write
|
|
*
|
|
* Append @size bytes from @data. @dbuf expands if necessary.
|
|
*
|
|
* Returns: %FALSE on out of memory, %TRUE otherwise.
|
|
*/
|
|
gboolean
|
|
vips_dbuf_write( VipsDbuf *dbuf, const unsigned char *data, size_t size )
|
|
{
|
|
if( !vips_dbuf_allocate( dbuf, size ) )
|
|
return( FALSE );
|
|
|
|
memcpy( dbuf->data + dbuf->write_point, data, size );
|
|
dbuf->write_point += size;
|
|
dbuf->data_size = VIPS_MAX( dbuf->data_size, dbuf->write_point );
|
|
|
|
return( TRUE );
|
|
}
|
|
|
|
/**
|
|
* vips_dbuf_writef:
|
|
* @dbuf: the buffer
|
|
* @fmt: <function>printf()</function>-style format string
|
|
* @...: arguments to format string
|
|
*
|
|
* Format the string and write to @dbuf.
|
|
*
|
|
* Returns: %FALSE on out of memory, %TRUE otherwise.
|
|
*/
|
|
gboolean
|
|
vips_dbuf_writef( VipsDbuf *dbuf, const char *fmt, ... )
|
|
{
|
|
va_list ap;
|
|
char *line;
|
|
|
|
va_start( ap, fmt );
|
|
line = g_strdup_vprintf( fmt, ap );
|
|
va_end( ap );
|
|
|
|
if( vips_dbuf_write( dbuf, (unsigned char *) line, strlen( line ) ) ) {
|
|
g_free( line );
|
|
return( FALSE );
|
|
}
|
|
g_free( line );
|
|
|
|
return( TRUE );
|
|
}
|
|
|
|
/**
|
|
* vips_dbuf_write_amp:
|
|
* @dbuf: the buffer
|
|
* @str: string to write
|
|
*
|
|
* Write @str to @dbuf, 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: %FALSE on out of memory, %TRUE otherwise.
|
|
*/
|
|
gboolean
|
|
vips_dbuf_write_amp( VipsDbuf *dbuf, 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 "%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_dbuf_writef( dbuf, "&#x%04x;", 0x2400 + *p ) )
|
|
return( FALSE );
|
|
}
|
|
else if( *p == '<' ) {
|
|
if( !vips_dbuf_write( dbuf, (guchar *) "<", 4 ) )
|
|
return( FALSE );
|
|
}
|
|
else if( *p == '>' ) {
|
|
if( !vips_dbuf_write( dbuf, (guchar *) ">", 4 ) )
|
|
return( FALSE );
|
|
}
|
|
else if( *p == '&' ) {
|
|
if( !vips_dbuf_write( dbuf, (guchar *) "&", 5 ) )
|
|
return( FALSE );
|
|
}
|
|
else {
|
|
if( !vips_dbuf_write( dbuf, (guchar *) p, 1 ) )
|
|
return( FALSE );
|
|
}
|
|
|
|
return( TRUE );
|
|
}
|
|
|
|
/**
|
|
* vips_dbuf_reset:
|
|
* @dbuf: the buffer
|
|
*
|
|
* Reset the buffer to empty. No memory is freed, just the data size and
|
|
* write point are reset.
|
|
*/
|
|
void
|
|
vips_dbuf_reset( VipsDbuf *dbuf )
|
|
{
|
|
dbuf->write_point = 0;
|
|
dbuf->data_size = 0;
|
|
}
|
|
|
|
/**
|
|
* vips_dbuf_destroy:
|
|
* @dbuf: the buffer
|
|
*
|
|
* Destroy @dbuf. This frees any allocated memory.
|
|
*/
|
|
void
|
|
vips_dbuf_destroy( VipsDbuf *dbuf )
|
|
{
|
|
vips_dbuf_reset( dbuf );
|
|
|
|
VIPS_FREE( dbuf->data );
|
|
dbuf->allocated_size = 0;
|
|
}
|
|
|
|
/**
|
|
* vips_dbuf_seek:
|
|
* @dbuf: the buffer
|
|
* @offset: how to move the write point
|
|
* @whence: from start, from end, from current
|
|
*
|
|
* Move the write point. @whence can be %SEEK_SET, %SEEK_CUR, %SEEK_END, with
|
|
* the usual meaning.
|
|
*/
|
|
gboolean
|
|
vips_dbuf_seek( VipsDbuf *dbuf, off_t offset, int whence )
|
|
{
|
|
off_t new_write_point;
|
|
|
|
switch( whence ) {
|
|
case SEEK_SET:
|
|
new_write_point = offset;
|
|
break;
|
|
|
|
case SEEK_END:
|
|
new_write_point = dbuf->data_size + offset;
|
|
break;
|
|
|
|
case SEEK_CUR:
|
|
new_write_point = dbuf->write_point + offset;
|
|
break;
|
|
|
|
default:
|
|
g_assert( 0 );
|
|
new_write_point = dbuf->write_point;
|
|
break;
|
|
}
|
|
|
|
if( new_write_point < 0 ) {
|
|
vips_error( "VipsDbuf", "%s", "negative seek" );
|
|
return( FALSE );
|
|
}
|
|
|
|
/* Possibly need to grow the buffer
|
|
*/
|
|
if( !vips_dbuf_minimum_size( dbuf, new_write_point ) )
|
|
return( FALSE );
|
|
dbuf->write_point = new_write_point;
|
|
if( dbuf->data_size < dbuf->write_point ) {
|
|
memset( dbuf->data + dbuf->data_size, 0,
|
|
dbuf->write_point - dbuf->data_size );
|
|
dbuf->data_size = dbuf->write_point;
|
|
}
|
|
|
|
return( TRUE );
|
|
}
|
|
|
|
/**
|
|
* vips_dbuf_truncate:
|
|
* @dbuf: the buffer
|
|
*
|
|
* Truncate the data so that it ends at the write point. No memory is freed.
|
|
*/
|
|
void
|
|
vips_dbuf_truncate( VipsDbuf *dbuf )
|
|
{
|
|
dbuf->data_size = dbuf->write_point;
|
|
}
|
|
|
|
/**
|
|
* vips_dbuf_tell:
|
|
* @dbuf: the buffer
|
|
*
|
|
* Returns: the current write point
|
|
*/
|
|
off_t
|
|
vips_dbuf_tell( VipsDbuf *dbuf )
|
|
{
|
|
return( dbuf->write_point );
|
|
}
|
|
|
|
/**
|
|
* vips_dbuf_null_terminate:
|
|
* @dbuf: the buffer
|
|
*
|
|
* Make sure the byte after the last data byte is `\0`. This extra byte is not
|
|
* included in the data size and the write point is not moved.
|
|
*
|
|
* This makes it safe to treat the dbuf contents as a C string.
|
|
*
|
|
* Returns: %FALSE on out of memory, %TRUE otherwise.
|
|
*/
|
|
static gboolean
|
|
vips_dbuf_null_terminate( VipsDbuf *dbuf )
|
|
{
|
|
if( !vips_dbuf_minimum_size( dbuf, dbuf->data_size + 1 ) )
|
|
return( FALSE );
|
|
|
|
dbuf->data[dbuf->data_size] = 0;
|
|
|
|
return( TRUE );
|
|
}
|
|
|
|
/**
|
|
* vips_dbuf_steal:
|
|
* @dbuf: the buffer
|
|
* @size: (allow-none): optionally return length in bytes here
|
|
*
|
|
* Destroy a buffer, but rather than freeing memory, a pointer is returned.
|
|
* This must be freed with g_free().
|
|
*
|
|
* A `\0` is appended, but not included in the character count. This is so the
|
|
* pointer can be safely treated as a C string.
|
|
*
|
|
* Returns: (transfer full): The pointer held by @dbuf.
|
|
*/
|
|
unsigned char *
|
|
vips_dbuf_steal( VipsDbuf *dbuf, size_t *size )
|
|
{
|
|
unsigned char *data;
|
|
|
|
vips_dbuf_null_terminate( dbuf );
|
|
|
|
data = dbuf->data;
|
|
|
|
if( size )
|
|
*size = dbuf->data_size;
|
|
|
|
dbuf->data = NULL;
|
|
vips_dbuf_destroy( dbuf );
|
|
|
|
return( data );
|
|
}
|
|
|
|
/**
|
|
* vips_dbuf_string:
|
|
* @dbuf: the buffer
|
|
* @size: (allow-none): optionally return length in bytes here
|
|
*
|
|
* Return a pointer to @dbuf's internal data.
|
|
*
|
|
* A `\0` is appended, but not included in the character count. This is so the
|
|
* pointer can be safely treated as a C string.
|
|
*
|
|
* Returns: (transfer none): The pointer held by @dbuf.
|
|
*/
|
|
unsigned char *
|
|
vips_dbuf_string( VipsDbuf *dbuf, size_t *size )
|
|
{
|
|
vips_dbuf_null_terminate( dbuf );
|
|
|
|
if( size )
|
|
*size = dbuf->data_size;
|
|
|
|
return( dbuf->data );
|
|
}
|
|
|
|
|