[master] Implement two-pass png_decompress_chunk() function

suggested by John Bowler.  Does not waste memory, does only one malloc()
to the measured decompressed size.
This commit is contained in:
Glenn Randers-Pehrson 2010-01-28 07:16:15 -06:00
parent a69064328b
commit 877b08d6d2
2 changed files with 76 additions and 82 deletions

View File

@ -1,7 +1,7 @@
/* pngpriv.h - private declarations for use inside libpng
*
* libpng version 1.4.1beta05 - January 26, 2010
* libpng version 1.4.1beta06 - January 28, 2010
* For conditions of distribution and use, see copyright notice in png.h
* Copyright (c) 1998-2010 Glenn Randers-Pehrson
* (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger)
@ -306,6 +306,8 @@ PNG_EXTERN void png_crc_read PNGARG((png_structp png_ptr, png_bytep buf,
/* Decompress data in a chunk that uses compression */
#if defined(PNG_zTXt_SUPPORTED) || defined(PNG_iTXt_SUPPORTED) || \
defined(PNG_iCCP_SUPPORTED) || defined(PNG_sPLT_SUPPORTED)
PNG_EXTERN png_size_t png_measure_decompressed_chunk PNGARG((png_structp
png_ptr, int comp_type, png_size_t chunklength, png_size_t prefix_length));
PNG_EXTERN void png_decompress_chunk PNGARG((png_structp png_ptr,
int comp_type, png_size_t chunklength, png_size_t prefix_length,
png_size_t *data_length));

View File

@ -1,7 +1,7 @@
/* pngrutil.c - utilities to read a PNG file
*
* Last changed in libpng 1.4.1 [January 26, 2010]
* Last changed in libpng 1.4.1 [January 28, 2010]
* Copyright (c) 1998-2010 Glenn Randers-Pehrson
* (Version 0.96 Copyright (c) 1996, 1997 Andreas Dilger)
* (Version 0.88 Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.)
@ -201,6 +201,68 @@ png_crc_error(png_structp png_ptr)
#if defined(PNG_READ_zTXt_SUPPORTED) || defined(PNG_READ_iTXt_SUPPORTED) || \
defined(PNG_READ_iCCP_SUPPORTED)
png_size_t
png_measure_decompressed_chunk(png_structp png_ptr, int comp_type,
png_size_t chunklength, png_size_t prefix_size)
{
png_charp text;
png_charp test = "X";
png_size_t text_size = 0;
if (comp_type == PNG_COMPRESSION_TYPE_BASE)
{
int ret = Z_OK;
png_ptr->zstream.next_in = (png_bytep)(png_ptr->chunkdata + prefix_size);
png_ptr->zstream.avail_in = (uInt)(chunklength - prefix_size);
png_ptr->zstream.next_out = png_ptr->zbuf;
png_ptr->zstream.avail_out = (uInt)png_ptr->zbuf_size;
text = NULL;
while (png_ptr->zstream.avail_in)
{
ret = inflate(&png_ptr->zstream, Z_PARTIAL_FLUSH);
if (ret != Z_OK && ret != Z_STREAM_END)
{
inflateReset(&png_ptr->zstream);
png_ptr->zstream.avail_in = 0;
break;
}
if (!png_ptr->zstream.avail_out || ret == Z_STREAM_END)
{
if (text == NULL) /* Initialize the decompression buffer */
{
text_size = prefix_size +
png_ptr->zbuf_size - png_ptr->zstream.avail_out;
text=test;
}
else /* Enlarge the decompression buffer */
{
text_size += png_ptr->zbuf_size - png_ptr->zstream.avail_out;
#ifdef PNG_CHUNK_MALLOC_LIMIT_SUPPORTED
if (text_size >= png_ptr->user_chunk_malloc_max - 1)
return 0;
#endif
}
}
if (ret == Z_STREAM_END)
break;
else
{
png_ptr->zstream.next_out = png_ptr->zbuf;
png_ptr->zstream.avail_out = (uInt)png_ptr->zbuf_size;
}
}
inflateReset(&png_ptr->zstream);
png_ptr->zstream.avail_in = 0;
}
return text_size;
}
/*
* Decompress trailing data in a chunk. The assumption is that chunkdata
* points at an allocated area holding the contents of a chunk with a
@ -216,6 +278,15 @@ png_decompress_chunk(png_structp png_ptr, int comp_type,
static PNG_CONST char msg[] = "Error decoding compressed chunk";
png_charp text;
png_size_t text_size;
png_size_t expanded_size;
expanded_size= png_measure_decompressed_chunk(png_ptr, comp_type,
chunklength, prefix_size);
if (expanded_size == 0)
{
*newlength=0;
return;
}
if (comp_type == PNG_COMPRESSION_TYPE_BASE)
{
@ -274,8 +345,7 @@ png_decompress_chunk(png_structp png_ptr, int comp_type,
{
if (text == NULL) /* Initialize the decompression buffer */
{
text_size = prefix_size +
png_ptr->zbuf_size - png_ptr->zstream.avail_out;
text_size = expanded_size;
text = (png_charp)png_malloc_warn(png_ptr, text_size + 1);
if (text == NULL)
@ -291,64 +361,6 @@ png_decompress_chunk(png_structp png_ptr, int comp_type,
*(text + text_size) = 0x00;
buffer_size = text_size;
}
else /* Enlarge the decompression buffer */
{
png_charp tmp = text;
png_size_t new_text_size;
new_text_size = text_size + png_ptr->zbuf_size -
png_ptr->zstream.avail_out;
if (new_text_size > buffer_size)
{
if (png_ptr->zstream.avail_out)
buffer_size = new_text_size;
else
buffer_size += buffer_size;
#ifdef PNG_CHUNK_MALLOC_LIMIT_SUPPORTED
if (buffer_size >= png_ptr->user_chunk_malloc_max - 1)
{
if (new_text_size >=
png_ptr->user_chunk_malloc_max - 1)
{
png_free(png_ptr, tmp);
png_warning(png_ptr,
"No space for decompressed chunk");
text = NULL;
}
else
{
buffer_size = png_ptr->user_chunk_malloc_max - 1;
text = (png_charp)png_malloc_warn(png_ptr,
buffer_size + 1);
}
}
else
text = (png_charp)png_malloc_warn(png_ptr,
buffer_size + 1);
#else
text = (png_charp)png_malloc_warn(png_ptr,
buffer_size + 1);
#endif
if (text == NULL)
{
png_warning(png_ptr,
"Not enough memory to decompress chunk");
break;
}
png_memcpy(text, tmp, text_size);
png_free(png_ptr, tmp);
}
/* FIX ME: zTXt chunk written by pngtest is 6 bytes too large */
png_memcpy(text + text_size, png_ptr->zbuf,
(png_ptr->zbuf_size - png_ptr->zstream.avail_out));
text_size += png_ptr->zbuf_size - png_ptr->zstream.avail_out;
*(text + text_size) = 0x00;
}
}
if (ret == Z_STREAM_END)
break;
@ -360,26 +372,6 @@ png_decompress_chunk(png_structp png_ptr, int comp_type,
}
}
if (text != NULL && buffer_size > text_size)
{
/* Reduce text allocation to actual size */
png_charp tmp;
tmp = text;
text = (png_charp)png_malloc_warn(png_ptr,
text_size);
if (text == NULL)
text = tmp;
else
{
png_memcpy(text, tmp, text_size + 1);
png_free(png_ptr, tmp);
}
}
if (ret != Z_STREAM_END)
{
#ifdef PNG_STDIO_SUPPORTED