mirror of
https://github.com/cuberite/libdeflate.git
synced 2025-09-09 12:16:42 -04:00
Make decompress API functions return a result code rather than a 'bool'
This commit is contained in:
parent
be419e24fa
commit
ee1535ecc1
63
libdeflate.h
63
libdeflate.h
@ -9,7 +9,6 @@
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
|
||||
/* ========================================================================== */
|
||||
@ -124,26 +123,56 @@ struct deflate_decompressor;
|
||||
extern struct deflate_decompressor *
|
||||
deflate_alloc_decompressor(void);
|
||||
|
||||
/*
|
||||
* Result of a call to deflate_decompress(), zlib_decompress(), or
|
||||
* gzip_decompress().
|
||||
*/
|
||||
enum decompress_result {
|
||||
/* Decompression was successful. */
|
||||
DECOMPRESS_SUCCESS = 0,
|
||||
|
||||
/* Decompressed failed because the compressed data was invalid, corrupt,
|
||||
* or otherwise unsupported. */
|
||||
DECOMPRESS_BAD_DATA = 1,
|
||||
|
||||
/* A NULL 'actual_out_nbytes_ret' was provided, but the data would have
|
||||
* decompressed to fewer than 'out_nbytes_avail' bytes. */
|
||||
DECOMPRESS_SHORT_OUTPUT = 2,
|
||||
|
||||
/* The data would have decompressed to more than 'out_nbytes_avail'
|
||||
* bytes. */
|
||||
DECOMPRESS_INSUFFICIENT_SPACE = 3,
|
||||
};
|
||||
|
||||
/*
|
||||
* deflate_decompress() decompresses 'in_nbytes' bytes of DEFLATE-compressed
|
||||
* data at 'in' and writes the uncompressed data to 'out', which is a buffer of
|
||||
* at least 'out_nbytes_avail' bytes. If decompression was successful, then
|
||||
* true is returned; otherwise, false is returned.
|
||||
* at least 'out_nbytes_avail' bytes. If decompression was successful, then 0
|
||||
* (DECOMPRESS_SUCCESS) is returned; otherwise, a nonzero result code such as
|
||||
* DECOMPRESS_BAD_DATA is returned. If a nonzero result code is returned, then
|
||||
* the contents of the output buffer are undefined.
|
||||
*
|
||||
* deflate_decompress() can be used in cases where the actual uncompressed size
|
||||
* is known (recommended) or unknown (not recommended). If the actual
|
||||
* uncompressed size is known, then pass the actual uncompressed size as
|
||||
* 'out_nbytes_avail' and pass NULL for 'actual_out_nbytes_ret'. This makes
|
||||
* deflate_decompress() return false if the data did not decompress to exactly
|
||||
* the specified number of bytes. Alternatively, if the actual uncompressed
|
||||
* size is unknown, then provide a non-NULL 'actual_out_nbytes_ret' and provide
|
||||
* a buffer with some size 'out_nbytes_avail' that you think is large enough to
|
||||
* hold all the uncompressed data. In this case, if the data decompresses to
|
||||
* less than or equal to 'out_nbytes_avail' bytes, then deflate_decompress()
|
||||
* will write the actual uncompressed size to *actual_out_nbytes_ret and return
|
||||
* true. Otherwise, it will return false.
|
||||
* is known (recommended) or unknown (not recommended):
|
||||
*
|
||||
* - If the actual uncompressed size is known, then pass the actual
|
||||
* uncompressed size as 'out_nbytes_avail' and pass NULL for
|
||||
* 'actual_out_nbytes_ret'. This makes deflate_decompress() fail with
|
||||
* DECOMPRESS_SHORT_OUTPUT if the data decompressed to fewer than the
|
||||
* specified number of bytes.
|
||||
*
|
||||
* - If the actual uncompressed size is unknown, then provide a non-NULL
|
||||
* 'actual_out_nbytes_ret' and provide a buffer with some size
|
||||
* 'out_nbytes_avail' that you think is large enough to hold all the
|
||||
* uncompressed data. In this case, if the data decompresses to less than
|
||||
* or equal to 'out_nbytes_avail' bytes, then deflate_decompress() will
|
||||
* write the actual uncompressed size to *actual_out_nbytes_ret and return 0
|
||||
* (DECOMPRESS_SUCCESS). Otherwise, it will return
|
||||
* DECOMPRESS_INSUFFICIENT_SPACE if the provided buffer was not large enough
|
||||
* but no other problems were encountered, or another nonzero result code if
|
||||
* decompression failed for another reason.
|
||||
*/
|
||||
extern bool
|
||||
extern enum decompress_result
|
||||
deflate_decompress(struct deflate_decompressor *decompressor,
|
||||
const void *in, size_t in_nbytes,
|
||||
void *out, size_t out_nbytes_avail,
|
||||
@ -153,7 +182,7 @@ deflate_decompress(struct deflate_decompressor *decompressor,
|
||||
* Like deflate_decompress(), but assumes the zlib wrapper format instead of raw
|
||||
* DEFLATE.
|
||||
*/
|
||||
extern bool
|
||||
extern enum decompress_result
|
||||
zlib_decompress(struct deflate_decompressor *decompressor,
|
||||
const void *in, size_t in_nbytes,
|
||||
void *out, size_t out_nbytes_avail,
|
||||
@ -163,7 +192,7 @@ zlib_decompress(struct deflate_decompressor *decompressor,
|
||||
* Like deflate_decompress(), but assumes the gzip wrapper format instead of raw
|
||||
* DEFLATE.
|
||||
*/
|
||||
extern bool
|
||||
extern enum decompress_result
|
||||
gzip_decompress(struct deflate_decompressor *decompressor,
|
||||
const void *in, size_t in_nbytes,
|
||||
void *out, size_t out_nbytes_avail,
|
||||
|
@ -6,7 +6,7 @@
|
||||
* sets.
|
||||
*/
|
||||
|
||||
static bool ATTRIBUTES
|
||||
static enum decompress_result ATTRIBUTES
|
||||
FUNCNAME(struct deflate_decompressor * restrict d,
|
||||
const void * restrict in, size_t in_nbytes,
|
||||
void * restrict out, size_t out_nbytes_avail,
|
||||
@ -176,7 +176,8 @@ next_block:
|
||||
nlen = READ_U16();
|
||||
|
||||
SAFETY_CHECK(len == (u16)~nlen);
|
||||
SAFETY_CHECK(len <= out_end - out_next);
|
||||
if (unlikely(len > out_end - out_next))
|
||||
return DECOMPRESS_INSUFFICIENT_SPACE;
|
||||
SAFETY_CHECK(len <= in_end - in_next);
|
||||
|
||||
memcpy(out_next, in_next, len);
|
||||
@ -236,7 +237,8 @@ next_block:
|
||||
REMOVE_BITS(entry & HUFFDEC_LENGTH_MASK);
|
||||
if (entry & HUFFDEC_LITERAL) {
|
||||
/* Literal */
|
||||
SAFETY_CHECK(out_next < out_end);
|
||||
if (unlikely(out_next == out_end))
|
||||
return DECOMPRESS_INSUFFICIENT_SPACE;
|
||||
*out_next++ = (u8)(entry >> HUFFDEC_RESULT_SHIFT);
|
||||
continue;
|
||||
}
|
||||
@ -258,7 +260,8 @@ next_block:
|
||||
* SIZE_MAX. */
|
||||
STATIC_ASSERT(HUFFDEC_END_OF_BLOCK_LENGTH == 0);
|
||||
if (unlikely((size_t)length - 1 > out_end - out_next)) {
|
||||
SAFETY_CHECK(length == HUFFDEC_END_OF_BLOCK_LENGTH);
|
||||
if (unlikely(length != HUFFDEC_END_OF_BLOCK_LENGTH))
|
||||
return DECOMPRESS_INSUFFICIENT_SPACE;
|
||||
goto block_done;
|
||||
}
|
||||
|
||||
@ -361,9 +364,11 @@ block_done:
|
||||
|
||||
/* That was the last block. */
|
||||
|
||||
if (!actual_out_nbytes_ret)
|
||||
return out_next == out_end;
|
||||
|
||||
*actual_out_nbytes_ret = out_next - (u8 *)out;
|
||||
return true;
|
||||
if (actual_out_nbytes_ret) {
|
||||
*actual_out_nbytes_ret = out_next - (u8 *)out;
|
||||
} else {
|
||||
if (out_next != out_end)
|
||||
return DECOMPRESS_SHORT_OUTPUT;
|
||||
}
|
||||
return DECOMPRESS_SUCCESS;
|
||||
}
|
||||
|
@ -37,14 +37,15 @@
|
||||
#include "x86_cpu_features.h"
|
||||
|
||||
/* By default, if the expression passed to SAFETY_CHECK() evaluates to false,
|
||||
* then deflate_decompress() immediately returns false as the compressed data is
|
||||
* invalid. But if unsafe decompression is enabled, then the value of the
|
||||
* expression is ignored, allowing the compiler to optimize out some code. */
|
||||
* then deflate_decompress() immediately returns DECOMPRESS_BAD_DATA as the
|
||||
* compressed data is invalid. But if unsafe decompression is enabled, then the
|
||||
* value of the expression is ignored, allowing the compiler to optimize out
|
||||
* some code. */
|
||||
#if UNSAFE_DECOMPRESSION
|
||||
# warning "UNSAFE DECOMPRESSION IS ENABLED. THIS MUST ONLY BE USED IF THE DECOMPRESSOR INPUT WILL ALWAYS BE TRUSTED!"
|
||||
# define SAFETY_CHECK(expr) (void)(expr)
|
||||
#else
|
||||
# define SAFETY_CHECK(expr) if (unlikely(!(expr))) return false
|
||||
# define SAFETY_CHECK(expr) if (unlikely(!(expr))) return DECOMPRESS_BAD_DATA
|
||||
#endif
|
||||
|
||||
/*
|
||||
@ -814,20 +815,21 @@ copy_word_unaligned(const void *src, void *dst)
|
||||
|
||||
#if DISPATCH_ENABLED
|
||||
|
||||
static bool
|
||||
static enum decompress_result
|
||||
dispatch(struct deflate_decompressor * restrict d,
|
||||
const void * restrict in, size_t in_nbytes,
|
||||
void * restrict out, size_t out_nbytes_avail,
|
||||
size_t *actual_out_nbytes_ret);
|
||||
|
||||
typedef bool (*decompress_func_t)(struct deflate_decompressor * restrict d,
|
||||
const void * restrict in, size_t in_nbytes,
|
||||
void * restrict out, size_t out_nbytes_avail,
|
||||
size_t *actual_out_nbytes_ret);
|
||||
typedef enum decompress_result (*decompress_func_t)
|
||||
(struct deflate_decompressor * restrict d,
|
||||
const void * restrict in, size_t in_nbytes,
|
||||
void * restrict out, size_t out_nbytes_avail,
|
||||
size_t *actual_out_nbytes_ret);
|
||||
|
||||
static decompress_func_t decompress_impl = dispatch;
|
||||
|
||||
static bool
|
||||
static enum decompress_result
|
||||
dispatch(struct deflate_decompressor * restrict d,
|
||||
const void * restrict in, size_t in_nbytes,
|
||||
void * restrict out, size_t out_nbytes_avail,
|
||||
@ -853,7 +855,7 @@ dispatch(struct deflate_decompressor * restrict d,
|
||||
* calling the appropriate implementation depending on the CPU features at
|
||||
* runtime.
|
||||
*/
|
||||
LIBEXPORT bool
|
||||
LIBEXPORT enum decompress_result
|
||||
deflate_decompress(struct deflate_decompressor * restrict d,
|
||||
const void * restrict in, size_t in_nbytes,
|
||||
void * restrict out, size_t out_nbytes_avail,
|
||||
|
@ -14,7 +14,7 @@
|
||||
#include "gzip_constants.h"
|
||||
#include "unaligned.h"
|
||||
|
||||
LIBEXPORT bool
|
||||
LIBEXPORT enum decompress_result
|
||||
gzip_decompress(struct deflate_decompressor *d,
|
||||
const void *in, size_t in_nbytes,
|
||||
void *out, size_t out_nbytes_avail,
|
||||
@ -24,19 +24,20 @@ gzip_decompress(struct deflate_decompressor *d,
|
||||
const u8 * const in_end = in_next + in_nbytes;
|
||||
u8 flg;
|
||||
size_t actual_out_nbytes;
|
||||
enum decompress_result result;
|
||||
|
||||
if (in_nbytes < GZIP_MIN_OVERHEAD)
|
||||
return false;
|
||||
return DECOMPRESS_BAD_DATA;
|
||||
|
||||
/* ID1 */
|
||||
if (*in_next++ != GZIP_ID1)
|
||||
return false;
|
||||
return DECOMPRESS_BAD_DATA;
|
||||
/* ID2 */
|
||||
if (*in_next++ != GZIP_ID2)
|
||||
return false;
|
||||
return DECOMPRESS_BAD_DATA;
|
||||
/* CM */
|
||||
if (*in_next++ != GZIP_CM_DEFLATE)
|
||||
return false;
|
||||
return DECOMPRESS_BAD_DATA;
|
||||
flg = *in_next++;
|
||||
/* MTIME */
|
||||
in_next += 4;
|
||||
@ -46,7 +47,7 @@ gzip_decompress(struct deflate_decompressor *d,
|
||||
in_next += 1;
|
||||
|
||||
if (flg & GZIP_FRESERVED)
|
||||
return false;
|
||||
return DECOMPRESS_BAD_DATA;
|
||||
|
||||
/* Extra field */
|
||||
if (flg & GZIP_FEXTRA) {
|
||||
@ -54,7 +55,7 @@ gzip_decompress(struct deflate_decompressor *d,
|
||||
in_next += 2;
|
||||
|
||||
if (in_end - in_next < (u32)xlen + GZIP_FOOTER_SIZE)
|
||||
return false;
|
||||
return DECOMPRESS_BAD_DATA;
|
||||
|
||||
in_next += xlen;
|
||||
}
|
||||
@ -64,7 +65,7 @@ gzip_decompress(struct deflate_decompressor *d,
|
||||
while (*in_next++ != 0 && in_next != in_end)
|
||||
;
|
||||
if (in_end - in_next < GZIP_FOOTER_SIZE)
|
||||
return false;
|
||||
return DECOMPRESS_BAD_DATA;
|
||||
}
|
||||
|
||||
/* File comment (zero terminated) */
|
||||
@ -72,20 +73,23 @@ gzip_decompress(struct deflate_decompressor *d,
|
||||
while (*in_next++ != 0 && in_next != in_end)
|
||||
;
|
||||
if (in_end - in_next < GZIP_FOOTER_SIZE)
|
||||
return false;
|
||||
return DECOMPRESS_BAD_DATA;
|
||||
}
|
||||
|
||||
/* CRC16 for gzip header */
|
||||
if (flg & GZIP_FHCRC) {
|
||||
in_next += 2;
|
||||
if (in_end - in_next < GZIP_FOOTER_SIZE)
|
||||
return false;
|
||||
return DECOMPRESS_BAD_DATA;
|
||||
}
|
||||
|
||||
/* Compressed data */
|
||||
if (!deflate_decompress(d, in_next, in_end - GZIP_FOOTER_SIZE - in_next,
|
||||
out, out_nbytes_avail, actual_out_nbytes_ret))
|
||||
return false;
|
||||
result = deflate_decompress(d, in_next,
|
||||
in_end - GZIP_FOOTER_SIZE - in_next,
|
||||
out, out_nbytes_avail,
|
||||
actual_out_nbytes_ret);
|
||||
if (result != DECOMPRESS_SUCCESS)
|
||||
return result;
|
||||
|
||||
if (actual_out_nbytes_ret)
|
||||
actual_out_nbytes = *actual_out_nbytes_ret;
|
||||
@ -96,12 +100,12 @@ gzip_decompress(struct deflate_decompressor *d,
|
||||
|
||||
/* CRC32 */
|
||||
if (crc32_gzip(out, actual_out_nbytes) != get_unaligned_le32(in_next))
|
||||
return false;
|
||||
return DECOMPRESS_BAD_DATA;
|
||||
in_next += 4;
|
||||
|
||||
/* ISIZE */
|
||||
if ((u32)actual_out_nbytes != get_unaligned_le32(in_next))
|
||||
return false;
|
||||
return DECOMPRESS_BAD_DATA;
|
||||
|
||||
return true;
|
||||
return DECOMPRESS_SUCCESS;
|
||||
}
|
||||
|
@ -14,7 +14,7 @@
|
||||
#include "unaligned.h"
|
||||
#include "zlib_constants.h"
|
||||
|
||||
LIBEXPORT bool
|
||||
LIBEXPORT enum decompress_result
|
||||
zlib_decompress(struct deflate_decompressor *d,
|
||||
const void *in, size_t in_nbytes,
|
||||
void *out, size_t out_nbytes_avail,
|
||||
@ -24,9 +24,10 @@ zlib_decompress(struct deflate_decompressor *d,
|
||||
const u8 * const in_end = in_next + in_nbytes;
|
||||
u16 hdr;
|
||||
size_t actual_out_nbytes;
|
||||
enum decompress_result result;
|
||||
|
||||
if (in_nbytes < ZLIB_MIN_OVERHEAD)
|
||||
return false;
|
||||
return DECOMPRESS_BAD_DATA;
|
||||
|
||||
/* 2 byte header: CMF and FLG */
|
||||
hdr = get_unaligned_be16(in_next);
|
||||
@ -34,24 +35,27 @@ zlib_decompress(struct deflate_decompressor *d,
|
||||
|
||||
/* FCHECK */
|
||||
if ((hdr % 31) != 0)
|
||||
return false;
|
||||
return DECOMPRESS_BAD_DATA;
|
||||
|
||||
/* CM */
|
||||
if (((hdr >> 8) & 0xF) != ZLIB_CM_DEFLATE)
|
||||
return false;
|
||||
return DECOMPRESS_BAD_DATA;
|
||||
|
||||
/* CINFO */
|
||||
if ((hdr >> 12) > ZLIB_CINFO_32K_WINDOW)
|
||||
return false;
|
||||
return DECOMPRESS_BAD_DATA;
|
||||
|
||||
/* FDICT */
|
||||
if ((hdr >> 5) & 1)
|
||||
return false;
|
||||
return DECOMPRESS_BAD_DATA;
|
||||
|
||||
/* Compressed data */
|
||||
if (!deflate_decompress(d, in_next, in_end - ZLIB_FOOTER_SIZE - in_next,
|
||||
out, out_nbytes_avail, actual_out_nbytes_ret))
|
||||
return false;
|
||||
result = deflate_decompress(d, in_next,
|
||||
in_end - ZLIB_FOOTER_SIZE - in_next,
|
||||
out, out_nbytes_avail,
|
||||
actual_out_nbytes_ret);
|
||||
if (result != DECOMPRESS_SUCCESS)
|
||||
return result;
|
||||
|
||||
if (actual_out_nbytes_ret)
|
||||
actual_out_nbytes = *actual_out_nbytes_ret;
|
||||
@ -62,7 +66,7 @@ zlib_decompress(struct deflate_decompressor *d,
|
||||
|
||||
/* ADLER32 */
|
||||
if (adler32(out, actual_out_nbytes) != get_unaligned_be32(in_next))
|
||||
return false;
|
||||
return DECOMPRESS_BAD_DATA;
|
||||
|
||||
return true;
|
||||
return DECOMPRESS_SUCCESS;
|
||||
}
|
||||
|
@ -12,6 +12,7 @@
|
||||
#undef _ANSI_SOURCE
|
||||
#define _POSIX_C_SOURCE 199309L
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <getopt.h>
|
||||
@ -181,14 +182,13 @@ compressor_destroy(struct compressor *c)
|
||||
|
||||
struct decompressor {
|
||||
void *private;
|
||||
bool (*decompress)(void *, const void *, size_t, void *, size_t, size_t *);
|
||||
bool (*decompress)(void *, const void *, size_t, void *, size_t);
|
||||
void (*free_private)(void *);
|
||||
};
|
||||
|
||||
static bool
|
||||
libz_decompress(void *private, const void *in, size_t in_nbytes,
|
||||
void *out, size_t out_nbytes_avail,
|
||||
size_t *actual_out_nbytes_ret)
|
||||
void *out, size_t out_nbytes_avail)
|
||||
{
|
||||
z_stream *z = private;
|
||||
|
||||
@ -209,6 +209,36 @@ libz_free_decompressor_private(void *private)
|
||||
free(private);
|
||||
}
|
||||
|
||||
static bool
|
||||
libdeflate_deflate_decompress(void *private, const void *in, size_t in_nbytes,
|
||||
void *out, size_t out_nbytes_avail)
|
||||
{
|
||||
return 0 == deflate_decompress(private, in, in_nbytes,
|
||||
out, out_nbytes_avail, NULL);
|
||||
}
|
||||
|
||||
static bool
|
||||
libdeflate_zlib_decompress(void *private, const void *in, size_t in_nbytes,
|
||||
void *out, size_t out_nbytes_avail)
|
||||
{
|
||||
return 0 == zlib_decompress(private, in, in_nbytes,
|
||||
out, out_nbytes_avail, NULL);
|
||||
}
|
||||
|
||||
static bool
|
||||
libdeflate_gzip_decompress(void *private, const void *in, size_t in_nbytes,
|
||||
void *out, size_t out_nbytes_avail)
|
||||
{
|
||||
return 0 == gzip_decompress(private, in, in_nbytes,
|
||||
out, out_nbytes_avail, NULL);
|
||||
}
|
||||
|
||||
static void
|
||||
libdeflate_free_decompressor_private(void *private)
|
||||
{
|
||||
deflate_free_decompressor(private);
|
||||
}
|
||||
|
||||
static void
|
||||
decompressor_init(struct decompressor *d, enum wrapper wrapper, bool use_libz)
|
||||
{
|
||||
@ -236,16 +266,16 @@ decompressor_init(struct decompressor *d, enum wrapper wrapper, bool use_libz)
|
||||
ASSERT(d->private != NULL, "out of memory");
|
||||
switch (wrapper) {
|
||||
case NO_WRAPPER:
|
||||
d->decompress = (void *)deflate_decompress;
|
||||
d->decompress = libdeflate_deflate_decompress;
|
||||
break;
|
||||
case ZLIB_WRAPPER:
|
||||
d->decompress = (void *)zlib_decompress;
|
||||
d->decompress = libdeflate_zlib_decompress;
|
||||
break;
|
||||
case GZIP_WRAPPER:
|
||||
d->decompress = (void *)gzip_decompress;
|
||||
d->decompress = libdeflate_gzip_decompress;
|
||||
break;
|
||||
}
|
||||
d->free_private = (void *)deflate_free_decompressor;
|
||||
d->free_private = libdeflate_free_decompressor_private;
|
||||
}
|
||||
}
|
||||
|
||||
@ -254,7 +284,7 @@ do_decompress(struct decompressor *d, const void *in, size_t in_nbytes,
|
||||
void *out, size_t out_nbytes_avail)
|
||||
{
|
||||
return (*d->decompress)(d->private, in, in_nbytes,
|
||||
out, out_nbytes_avail, NULL);
|
||||
out, out_nbytes_avail);
|
||||
}
|
||||
|
||||
static void
|
||||
|
Loading…
x
Reference in New Issue
Block a user