blob: a24374bf5c705353087a07018de9112bea649b3b [file] [log] [blame]
// Copyright 2017 The Wuffs Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ----------------
// Uncomment one of these #define lines to test and bench alternative mimic
// libraries (libdeflate or miniz) instead of zlib-the-library.
//
// These are collectively referred to as
// WUFFS_MIMICLIB_USE_XXX_INSTEAD_OF_ZLIB.
//
// #define WUFFS_MIMICLIB_USE_LIBDEFLATE_INSTEAD_OF_ZLIB 1
// #define WUFFS_MIMICLIB_USE_MINIZ_INSTEAD_OF_ZLIB 1
// -------------------------------- WUFFS_MIMICLIB_USE_XXX_INSTEAD_OF_ZLIB
#if defined(WUFFS_MIMICLIB_USE_LIBDEFLATE_INSTEAD_OF_ZLIB)
#include "libdeflate.h"
#define WUFFS_MIMICLIB_DEFLATE_DOES_NOT_SUPPORT_STREAMING 1
#define WUFFS_MIMICLIB_ZLIB_DOES_NOT_SUPPORT_DICTIONARIES 1
uint32_t global_mimiclib_deflate_unused_u32;
typedef enum libdeflate_result (*libdeflate_decompress_func)(
struct libdeflate_decompressor* decompressor,
const void* in,
size_t in_nbytes,
void* out,
size_t out_nbytes_avail,
size_t* actual_in_nbytes_ret,
size_t* actual_out_nbytes_ret);
const char* //
libdeflate_result_as_const_char_star(enum libdeflate_result r) {
switch (r) {
case LIBDEFLATE_SUCCESS:
return NULL;
case LIBDEFLATE_BAD_DATA:
return "libdeflate: bad data";
case LIBDEFLATE_SHORT_OUTPUT:
return "libdeflate: short output";
case LIBDEFLATE_INSUFFICIENT_SPACE:
return "libdeflate: insufficient space";
}
return "libdeflate: unknown error";
}
const char* //
mimic_bench_adler32(wuffs_base__io_buffer* dst,
wuffs_base__io_buffer* src,
uint32_t wuffs_initialize_flags,
uint64_t wlimit,
uint64_t rlimit) {
global_mimiclib_deflate_unused_u32 = 1;
while (src->meta.ri < src->meta.wi) {
uint8_t* ptr = src->data.ptr + src->meta.ri;
size_t len = src->meta.wi - src->meta.ri;
if (len > 0x7FFFFFFF) {
return "src length is too large";
} else if (len > rlimit) {
len = rlimit;
}
global_mimiclib_deflate_unused_u32 =
libdeflate_adler32(global_mimiclib_deflate_unused_u32, ptr, len);
src->meta.ri += len;
}
return NULL;
}
const char* //
mimic_bench_crc32_ieee(wuffs_base__io_buffer* dst,
wuffs_base__io_buffer* src,
uint32_t wuffs_initialize_flags,
uint64_t wlimit,
uint64_t rlimit) {
global_mimiclib_deflate_unused_u32 = 0;
while (src->meta.ri < src->meta.wi) {
uint8_t* ptr = src->data.ptr + src->meta.ri;
size_t len = src->meta.wi - src->meta.ri;
if (len > 0x7FFFFFFF) {
return "src length is too large";
} else if (len > rlimit) {
len = rlimit;
}
global_mimiclib_deflate_unused_u32 =
libdeflate_crc32(global_mimiclib_deflate_unused_u32, ptr, len);
src->meta.ri += len;
}
return NULL;
}
const char* //
mimic_deflate_gzip_zlib_decode(wuffs_base__io_buffer* dst,
wuffs_base__io_buffer* src,
uint32_t wuffs_initialize_flags,
uint64_t wlimit,
uint64_t rlimit,
libdeflate_decompress_func func) {
struct libdeflate_decompressor* dec = libdeflate_alloc_decompressor();
if (!dec) {
return "libdeflate: alloc failed";
}
size_t n_dst = 0;
size_t n_src = 0;
enum libdeflate_result res =
(*func)(dec, wuffs_base__io_buffer__reader_pointer(src),
((size_t)wuffs_base__u64__min(
rlimit, wuffs_base__io_buffer__reader_length(src))),
wuffs_base__io_buffer__writer_pointer(dst),
((size_t)wuffs_base__u64__min(
wlimit, wuffs_base__io_buffer__writer_length(dst))),
&n_src, &n_dst);
if (res == LIBDEFLATE_SUCCESS) {
dst->meta.wi += n_dst;
src->meta.ri += n_src;
}
libdeflate_free_decompressor(dec);
return libdeflate_result_as_const_char_star(res);
}
const char* //
mimic_deflate_decode(wuffs_base__io_buffer* dst,
wuffs_base__io_buffer* src,
uint32_t wuffs_initialize_flags,
uint64_t wlimit,
uint64_t rlimit) {
return mimic_deflate_gzip_zlib_decode(dst, src, wuffs_initialize_flags,
wlimit, rlimit,
&libdeflate_deflate_decompress_ex);
}
const char* //
mimic_gzip_decode(wuffs_base__io_buffer* dst,
wuffs_base__io_buffer* src,
uint32_t wuffs_initialize_flags,
uint64_t wlimit,
uint64_t rlimit) {
return mimic_deflate_gzip_zlib_decode(dst, src, wuffs_initialize_flags,
wlimit, rlimit,
&libdeflate_gzip_decompress_ex);
}
const char* //
mimic_zlib_decode(wuffs_base__io_buffer* dst,
wuffs_base__io_buffer* src,
uint32_t wuffs_initialize_flags,
uint64_t wlimit,
uint64_t rlimit) {
return mimic_deflate_gzip_zlib_decode(dst, src, wuffs_initialize_flags,
wlimit, rlimit,
&libdeflate_zlib_decompress_ex);
}
const char* //
mimic_zlib_decode_with_dictionary(wuffs_base__io_buffer* dst,
wuffs_base__io_buffer* src,
wuffs_base__slice_u8 dictionary) {
return "libdeflate does not implement zlib dictionaries";
}
// -------------------------------- WUFFS_MIMICLIB_USE_XXX_INSTEAD_OF_ZLIB
#elif defined(WUFFS_MIMICLIB_USE_MINIZ_INSTEAD_OF_ZLIB)
#include "/path/to/your/copy/of/github.com/richgel999/miniz/miniz_tinfl.c"
// We deliberately do not define the
// WUFFS_MIMICLIB_DEFLATE_DOES_NOT_SUPPORT_STREAMING macro.
#define WUFFS_MIMICLIB_ZLIB_DOES_NOT_SUPPORT_DICTIONARIES 1
const char* //
mimic_bench_adler32(wuffs_base__io_buffer* dst,
wuffs_base__io_buffer* src,
uint32_t wuffs_initialize_flags,
uint64_t wlimit,
uint64_t rlimit) {
return "miniz does not independently compute Adler32";
}
const char* //
mimic_bench_crc32_ieee(wuffs_base__io_buffer* dst,
wuffs_base__io_buffer* src,
uint32_t wuffs_initialize_flags,
uint64_t wlimit,
uint64_t rlimit) {
return "miniz does not implement CRC32/IEEE";
}
const char* //
mimic_deflate_zlib_decode(wuffs_base__io_buffer* dst,
wuffs_base__io_buffer* src,
uint32_t wuffs_initialize_flags,
uint64_t wlimit,
uint64_t rlimit,
bool deflate_instead_of_zlib) {
if ((wlimit < UINT64_MAX) || (rlimit < UINT64_MAX)) {
// Supporting this would probably mean using tinfl_decompress instead of
// the simpler tinfl_decompress_mem_to_mem function.
return "unsupported I/O limit";
}
int flags = 0;
if (!deflate_instead_of_zlib) {
flags |= TINFL_FLAG_PARSE_ZLIB_HEADER;
}
size_t n = tinfl_decompress_mem_to_mem(
dst->data.ptr + dst->meta.wi, dst->data.len - dst->meta.wi,
src->data.ptr + src->meta.ri, src->meta.wi - src->meta.ri, flags);
if (n == TINFL_DECOMPRESS_MEM_TO_MEM_FAILED) {
return "TINFL_DECOMPRESS_MEM_TO_MEM_FAILED";
}
dst->meta.wi += n;
src->meta.ri = src->meta.wi;
return NULL;
}
const char* //
mimic_deflate_decode(wuffs_base__io_buffer* dst,
wuffs_base__io_buffer* src,
uint32_t wuffs_initialize_flags,
uint64_t wlimit,
uint64_t rlimit) {
return mimic_deflate_zlib_decode(dst, src, wuffs_initialize_flags, wlimit,
rlimit, true);
}
const char* //
mimic_gzip_decode(wuffs_base__io_buffer* dst,
wuffs_base__io_buffer* src,
uint32_t wuffs_initialize_flags,
uint64_t wlimit,
uint64_t rlimit) {
return "miniz does not implement gzip";
}
const char* //
mimic_zlib_decode(wuffs_base__io_buffer* dst,
wuffs_base__io_buffer* src,
uint32_t wuffs_initialize_flags,
uint64_t wlimit,
uint64_t rlimit) {
return mimic_deflate_zlib_decode(dst, src, wuffs_initialize_flags, wlimit,
rlimit, false);
}
const char* //
mimic_zlib_decode_with_dictionary(wuffs_base__io_buffer* dst,
wuffs_base__io_buffer* src,
wuffs_base__slice_u8 dictionary) {
return "miniz does not implement zlib dictionaries";
}
// -------------------------------- WUFFS_MIMICLIB_USE_XXX_INSTEAD_OF_ZLIB
#else
#include "zlib.h"
// We deliberately do not define the
// WUFFS_MIMICLIB_DEFLATE_DOES_NOT_SUPPORT_STREAMING macro.
// We deliberately do not define the
// WUFFS_MIMICLIB_ZLIB_DOES_NOT_SUPPORT_DICTIONARIES macro.
uint32_t global_mimiclib_deflate_unused_u32;
const char* //
mimic_bench_adler32(wuffs_base__io_buffer* dst,
wuffs_base__io_buffer* src,
uint32_t wuffs_initialize_flags,
uint64_t wlimit,
uint64_t rlimit) {
global_mimiclib_deflate_unused_u32 = 0;
while (src->meta.ri < src->meta.wi) {
uint8_t* ptr = src->data.ptr + src->meta.ri;
size_t len = src->meta.wi - src->meta.ri;
if (len > 0x7FFFFFFF) {
return "src length is too large";
} else if (len > rlimit) {
len = rlimit;
}
global_mimiclib_deflate_unused_u32 =
adler32(global_mimiclib_deflate_unused_u32, ptr, len);
src->meta.ri += len;
}
return NULL;
}
const char* //
mimic_bench_crc32_ieee(wuffs_base__io_buffer* dst,
wuffs_base__io_buffer* src,
uint32_t wuffs_initialize_flags,
uint64_t wlimit,
uint64_t rlimit) {
global_mimiclib_deflate_unused_u32 = 0;
while (src->meta.ri < src->meta.wi) {
uint8_t* ptr = src->data.ptr + src->meta.ri;
size_t len = src->meta.wi - src->meta.ri;
if (len > 0x7FFFFFFF) {
return "src length is too large";
} else if (len > rlimit) {
len = rlimit;
}
global_mimiclib_deflate_unused_u32 =
crc32(global_mimiclib_deflate_unused_u32, ptr, len);
src->meta.ri += len;
}
return NULL;
}
typedef enum {
zlib_flavor_raw,
zlib_flavor_gzip,
zlib_flavor_zlib,
} zlib_flavor;
const char* //
mimic_deflate_gzip_zlib_decode(wuffs_base__io_buffer* dst,
wuffs_base__io_buffer* src,
wuffs_base__slice_u8* dictionary,
uint64_t wlimit,
uint64_t rlimit,
zlib_flavor flavor) {
const char* ret = NULL;
if (dst->data.len > UINT_MAX) {
ret = "dst length is too large";
goto cleanup0;
}
if (src->data.len > UINT_MAX) {
ret = "src length is too large";
goto cleanup0;
}
// See inflateInit2 in the zlib manual, or in zlib.h, for details about how
// the window_bits int also encodes the wire format wrapper.
int window_bits = 0;
switch (flavor) {
case zlib_flavor_raw:
window_bits = -15;
break;
case zlib_flavor_gzip:
window_bits = +15 | 16;
break;
case zlib_flavor_zlib:
window_bits = +15;
break;
default:
ret = "invalid zlib_flavor";
goto cleanup0;
}
z_stream z = {0};
int ii2_err = inflateInit2(&z, window_bits);
if (ii2_err != Z_OK) {
ret = "inflateInit2 failed";
goto cleanup0;
}
while (true) {
z.next_in = src->data.ptr + src->meta.ri;
z.avail_in = src->meta.wi - src->meta.ri;
if (z.avail_in > rlimit) {
z.avail_in = rlimit;
}
uInt initial_avail_in = z.avail_in;
z.next_out = dst->data.ptr + dst->meta.wi;
z.avail_out = dst->data.len - dst->meta.wi;
if (z.avail_out > wlimit) {
z.avail_out = wlimit;
}
uInt initial_avail_out = z.avail_out;
// TODO: s/Z_NO_FLUSH/Z_SYNC_FLUSH/ more closely matches Wuffs' behavior.
int i_err = inflate(&z, Z_NO_FLUSH);
if (initial_avail_in < z.avail_in) {
ret = "inconsistent avail_in";
goto cleanup1;
}
src->meta.ri += initial_avail_in - z.avail_in;
if (initial_avail_out < z.avail_out) {
ret = "inconsistent avail_out";
goto cleanup1;
}
dst->meta.wi += initial_avail_out - z.avail_out;
if (i_err == Z_STREAM_END) {
break;
} else if (i_err == Z_NEED_DICT) {
if (dictionary) {
int isd_err =
inflateSetDictionary(&z, dictionary->ptr, dictionary->len);
if (isd_err != Z_OK) {
ret = "inflateSetDictionary failed";
goto cleanup1;
}
dictionary = NULL;
continue;
}
ret = "inflate failed (need dict)";
goto cleanup1;
} else if (i_err == Z_DATA_ERROR) {
ret = "inflate failed (data error)";
goto cleanup1;
} else if (i_err != Z_OK) {
ret = "inflate failed";
goto cleanup1;
}
}
cleanup1:;
int ie_err = inflateEnd(&z);
if ((ie_err != Z_OK) && !ret) {
ret = "inflateEnd failed";
}
cleanup0:;
return ret;
}
const char* //
mimic_deflate_decode(wuffs_base__io_buffer* dst,
wuffs_base__io_buffer* src,
uint32_t wuffs_initialize_flags,
uint64_t wlimit,
uint64_t rlimit) {
return mimic_deflate_gzip_zlib_decode(dst, src, NULL, wlimit, rlimit,
zlib_flavor_raw);
}
const char* //
mimic_gzip_decode(wuffs_base__io_buffer* dst,
wuffs_base__io_buffer* src,
uint32_t wuffs_initialize_flags,
uint64_t wlimit,
uint64_t rlimit) {
return mimic_deflate_gzip_zlib_decode(dst, src, NULL, wlimit, rlimit,
zlib_flavor_gzip);
}
const char* //
mimic_zlib_decode(wuffs_base__io_buffer* dst,
wuffs_base__io_buffer* src,
uint32_t wuffs_initialize_flags,
uint64_t wlimit,
uint64_t rlimit) {
return mimic_deflate_gzip_zlib_decode(dst, src, NULL, wlimit, rlimit,
zlib_flavor_zlib);
}
const char* //
mimic_zlib_decode_with_dictionary(wuffs_base__io_buffer* dst,
wuffs_base__io_buffer* src,
wuffs_base__slice_u8 dictionary) {
return mimic_deflate_gzip_zlib_decode(dst, src, &dictionary, UINT64_MAX,
UINT64_MAX, zlib_flavor_zlib);
}
#endif
// -------------------------------- WUFFS_MIMICLIB_USE_XXX_INSTEAD_OF_ZLIB