Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions src/codec_zlib.h
Original file line number Diff line number Diff line change
Expand Up @@ -51,12 +51,21 @@ struct _zlib_codec_data
* against a 254KB peak for a three-codec CD file. Heap-allocated rather
* than inline because chd_file embeds every codec's state by value. */
tinfl_decompressor * inflater;
/* Set when `inflater` is owned by the chd_file rather than by this codec.
* The CD codecs' subcode inflaters are one shared object - a hunk is
* decoded by exactly one CD codec, and tinfl_init() runs on entry to
* every decompress, so nothing carries across - and only the owner
* frees it. */
int borrowed;
#endif
};

/* zlib compression codec */
chd_error zlib_codec_init(void *codec, uint32_t hunkbytes);
void zlib_codec_free(void *codec);
#ifndef CHDR_SYSTEM_ZLIB
void zlib_codec_lend(void *codec, void *owner);
#endif
chd_error zlib_codec_decompress(void *codec, const uint8_t *src, uint32_t complen, uint8_t *dest, uint32_t destlen);

#endif /* LIBCHDR_CODEC_ZLIB_H */
69 changes: 69 additions & 0 deletions src/libchdr_chd.c
Original file line number Diff line number Diff line change
Expand Up @@ -326,6 +326,17 @@ struct _chd_file
avhuff_codec_data avhuff; /* avhuff codec data */
} codec_data;

#if WANT_SUBCODE && !defined(CHDR_SYSTEM_ZLIB)
/* One inflater for every CD codec's subcode stream. cdzl, cdlz and cdfl
* each used to allocate their own, but a hunk is decoded by exactly one
* CD codec and zlib_codec_decompress() runs tinfl_init() on entry, so
* nothing carries from one to the next - three tinfl_decompressor
* instances where one does. At CD geometry that is 16752 bytes back on a
* file using all three, 9.3% of its peak. cdzs is not here: its subcode
* goes through zstd. */
zlib_codec_data subcode_shared;
#endif

uint8_t * file_cache; /* cache of underlying file */

/* Compressed read-ahead. libchdr issues one seek+read per hunk, and
Expand Down Expand Up @@ -436,6 +447,9 @@ static chd_error header_read(chd_file *chd);
#endif
#endif

#if WANT_SUBCODE && !defined(CHDR_SYSTEM_ZLIB)
static chd_error cd_lend_subcode_inflater(chd_file *chd, uint32_t tag, void *codec);
#endif
static chd_error hunk_read_into_memory(chd_file *chd, uint32_t hunknum, uint8_t *dest);

/*-------------------------------------------------
Expand Down Expand Up @@ -1877,6 +1891,14 @@ static chd_error ensure_codec_ready(chd_file *chd, size_t slot, void *codec)
}
}

#if WANT_SUBCODE && !defined(CHDR_SYSTEM_ZLIB)
{
chd_error lerr = cd_lend_subcode_inflater(chd, chd->codecintf[slot]->compression, codec);
if (lerr != CHDERR_NONE)
return lerr;
}
#endif

if (chd->codecintf[slot]->init != NULL)
{
chd_error err = chd->codecintf[slot]->init(codec, chd->header.hunkbytes);
Expand Down Expand Up @@ -2163,6 +2185,13 @@ CHD_EXPORT chd_error chd_open_core_file_callbacks(const core_file_callbacks *cal
if (codec == NULL)
EARLY_EXIT(err = CHDERR_UNSUPPORTED_FORMAT);

#if WANT_SUBCODE && !defined(CHDR_SYSTEM_ZLIB)
err = cd_lend_subcode_inflater(newchd,
newchd->header.compression[decompnum], codec);
if (err != CHDERR_NONE)
EARLY_EXIT(err);
#endif

err = newchd->codecintf[decompnum]->init(codec, newchd->header.hunkbytes);
if (err != CHDERR_NONE)
EARLY_EXIT(err);
Expand Down Expand Up @@ -2318,6 +2347,12 @@ CHD_EXPORT void chd_close(chd_file *chd)
if (chd == NULL || chd->cookie != COOKIE_VALUE)
return;

#if WANT_SUBCODE && !defined(CHDR_SYSTEM_ZLIB)
/* The CD codecs only borrowed this one; free it here, once, after they
* have all dropped their pointers to it. */
zlib_codec_free(&chd->subcode_shared);
#endif

/* deinit the codec */
if (chd->header.version < 5)
{
Expand Down Expand Up @@ -3079,6 +3114,40 @@ static chd_error hunk_read_uncompressed(chd_file *chd, uint64_t offset, size_t s
return CHDERR_NONE;
}

#if WANT_SUBCODE && !defined(CHDR_SYSTEM_ZLIB)
/*-------------------------------------------------
cd_lend_subcode_inflater - seed a CD codec's
subcode inflater from the shared one
-------------------------------------------------*/

/* Seeds before the codec's own init runs: zlib_codec_init() keeps an inflater
* that is already marked borrowed, so nothing is allocated and then thrown
* away. Ordering-proof, which matters because LOWRAM_TARGET readies codecs
* lazily and in whatever order the hunks ask for them. */
static chd_error cd_lend_subcode_inflater(chd_file *chd, uint32_t tag, void *codec)
{
void *sub;

switch (tag)
{
case CHD_CODEC_CD_ZLIB: sub = &((cdzl_codec_data *)codec)->subcode_decompressor; break;
case CHD_CODEC_CD_LZMA: sub = &((cdlz_codec_data *)codec)->subcode_decompressor; break;
case CHD_CODEC_CD_FLAC: sub = &((cdfl_codec_data *)codec)->subcode_decompressor; break;
default: return CHDERR_NONE;
}

if (chd->subcode_shared.inflater == NULL)
{
chd_error err = zlib_codec_init(&chd->subcode_shared, 0);
if (err != CHDERR_NONE)
return err;
}

zlib_codec_lend(sub, &chd->subcode_shared);
return CHDERR_NONE;
}
#endif

/*-------------------------------------------------
hunk_read_into_memory - read a hunk into
memory at the given location
Expand Down
28 changes: 27 additions & 1 deletion src/libchdr_codec_zlib.c
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,37 @@ static void zlib_allocator_free(voidpf opaque);

/* ---- bundled miniz: drive tinfl directly, no 32KB dictionary ---- */

/*-------------------------------------------------
zlib_codec_lend - point this codec's inflater
at one owned elsewhere
-------------------------------------------------*/

void zlib_codec_lend(void *codec, void *owner)
{
zlib_codec_data *data = (zlib_codec_data *)codec;
zlib_codec_data *src = (zlib_codec_data *)owner;

data->inflater = src->inflater;
data->borrowed = 1;
}

chd_error zlib_codec_init(void *codec, uint32_t hunkbytes)
{
zlib_codec_data *data = (zlib_codec_data *)codec;

(void)hunkbytes;

/* A borrowed inflater is seeded before init runs; keep it across the
* wipe rather than allocating a second one. */
tinfl_decompressor *lent = data->borrowed ? data->inflater : NULL;

memset(data, 0, sizeof(zlib_codec_data));
if (lent != NULL)
{
data->inflater = lent;
data->borrowed = 1;
return CHDERR_NONE;
}
data->inflater = (tinfl_decompressor *)malloc(sizeof(tinfl_decompressor));
if (data->inflater == NULL)
return CHDERR_OUT_OF_MEMORY;
Expand All @@ -38,8 +62,10 @@ void zlib_codec_free(void *codec)

if (data != NULL && data->inflater != NULL)
{
free(data->inflater);
if (!data->borrowed)
free(data->inflater);
data->inflater = NULL;
data->borrowed = 0;
}
}

Expand Down
Loading