Skip to content

Commit

Permalink
IBM zSystems DFLTCC: Support inflate with small window
Browse files Browse the repository at this point in the history
There is no hardware control for DFLTCC window size, and because of
that supporting small windows for deflate is not trivial: one has to
make sure that DFLTCC does not emit large distances, which most likely
entails somehow trimming the window and/or input in order to make sure
that whave + avail_in <= wsize.

But inflate is much easier: one only has to allocate enough space. Do
that in dfltcc_alloc_window(), and also introduce ZCOPY_WINDOW() in
order to copy everything, not just what the software implementation
cares about.

After this change, software and hardware window formats no longer
match: the software will use wbits and wsize, and the hardware will use
HB_BITS and HB_SIZE. Unlike deflate, inflate does not switch between
software and hardware implementations mid-stream, which leaves only
inflateSetDictionary() and inflateGetDictionary() interesting.
  • Loading branch information
iii-i authored and Dead2 committed Dec 11, 2022
1 parent 506b438 commit 3eab317
Show file tree
Hide file tree
Showing 11 changed files with 216 additions and 61 deletions.
18 changes: 9 additions & 9 deletions arch/s390/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,15 +63,15 @@ integrated with the rest of zlib-ng using hook macros.
DFLTCC takes as arguments a parameter block, an input buffer, an output
buffer and a window. `ZALLOC_DEFLATE_STATE()`, `ZALLOC_INFLATE_STATE()`,
`ZFREE_STATE()`, `ZCOPY_DEFLATE_STATE()`, `ZCOPY_INFLATE_STATE()`,
`ZALLOC_WINDOW()` and `TRY_FREE_WINDOW()` macros encapsulate allocation
details for the parameter block (which is allocated alongside zlib-ng
state) and the window (which must be page-aligned).

While inflate software and hardware window formats match, this is not
the case for deflate. Therefore, `deflateSetDictionary()` and
`deflateGetDictionary()` need special handling, which is triggered using
`DEFLATE_SET_DICTIONARY_HOOK()` and `DEFLATE_GET_DICTIONARY_HOOK()`
macros.
`ZALLOC_WINDOW()`, `ZCOPY_WINDOW()` and `TRY_FREE_WINDOW()` macros encapsulate
allocation details for the parameter block (which is allocated alongside
zlib-ng state) and the window (which must be page-aligned and large enough).

Software and hardware window formats do not match, therefore,
`deflateSetDictionary()`, `deflateGetDictionary()`, `inflateSetDictionary()`
and `inflateGetDictionary()` need special handling, which is triggered using
`DEFLATE_SET_DICTIONARY_HOOK()`, `DEFLATE_GET_DICTIONARY_HOOK()`,
`INFLATE_SET_DICTIONARY_HOOK()` and `INFLATE_GET_DICTIONARY_HOOK()` macros.

`deflateResetKeep()` and `inflateResetKeep()` update the DFLTCC
parameter block using `DEFLATE_RESET_KEEP_HOOK()` and
Expand Down
8 changes: 6 additions & 2 deletions arch/s390/dfltcc_common.c
Original file line number Diff line number Diff line change
Expand Up @@ -20,16 +20,20 @@ void Z_INTERNAL *PREFIX(dfltcc_alloc_window)(PREFIX3(streamp) strm, uInt items,
void *w;

/* To simplify freeing, we store the pointer to the allocated buffer right
* before the window.
* before the window. Note that DFLTCC always uses HB_SIZE bytes.
*/
p = ZALLOC(strm, sizeof(void *) + items * size + PAGE_ALIGN, sizeof(unsigned char));
p = ZALLOC(strm, sizeof(void *) + MAX(items * size, HB_SIZE) + PAGE_ALIGN, sizeof(unsigned char));
if (p == NULL)
return NULL;
w = ALIGN_UP((char *)p + sizeof(void *), PAGE_ALIGN);
*(void **)((char *)w - sizeof(void *)) = p;
return w;
}

void Z_INTERNAL PREFIX(dfltcc_copy_window)(void *dest, const void *src, size_t n) {
memcpy(dest, src, MAX(n, HB_SIZE));
}

void Z_INTERNAL PREFIX(dfltcc_free_window)(PREFIX3(streamp) strm, void *w) {
if (w)
ZFREE(strm, *(void **)((unsigned char *)w - sizeof(void *)));
Expand Down
3 changes: 3 additions & 0 deletions arch/s390/dfltcc_common.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,15 @@
#include "zutil.h"

void Z_INTERNAL *PREFIX(dfltcc_alloc_window)(PREFIX3(streamp) strm, uInt items, uInt size);
void Z_INTERNAL PREFIX(dfltcc_copy_window)(void *dest, const void *src, size_t n);
void Z_INTERNAL PREFIX(dfltcc_free_window)(PREFIX3(streamp) strm, void *w);

#define ZFREE_STATE ZFREE

#define ZALLOC_WINDOW PREFIX(dfltcc_alloc_window)

#define ZCOPY_WINDOW PREFIX(dfltcc_copy_window)

#define ZFREE_WINDOW PREFIX(dfltcc_free_window)

#define TRY_FREE_WINDOW PREFIX(dfltcc_free_window)
Expand Down
42 changes: 2 additions & 40 deletions arch/s390/dfltcc_deflate.c
Original file line number Diff line number Diff line change
Expand Up @@ -376,36 +376,6 @@ int Z_INTERNAL PREFIX(dfltcc_can_set_reproducible)(PREFIX3(streamp) strm, int re
/*
Preloading history.
*/
static void append_history(struct dfltcc_param_v0 *param, unsigned char *history, const unsigned char *buf, uInt count) {
size_t offset;
size_t n;

/* Do not use more than 32K */
if (count > HB_SIZE) {
buf += count - HB_SIZE;
count = HB_SIZE;
}
offset = (param->ho + param->hl) % HB_SIZE;
if (offset + count <= HB_SIZE)
/* Circular history buffer does not wrap - copy one chunk */
memcpy(history + offset, buf, count);
else {
/* Circular history buffer wraps - copy two chunks */
n = HB_SIZE - offset;
memcpy(history + offset, buf, n);
memcpy(history, buf + n, count - n);
}
n = param->hl + count;
if (n <= HB_SIZE)
/* All history fits into buffer - no need to discard anything */
param->hl = n;
else {
/* History does not fit into buffer - discard extra bytes */
param->ho = (param->ho + (n - HB_SIZE)) % HB_SIZE;
param->hl = HB_SIZE;
}
}

int Z_INTERNAL PREFIX(dfltcc_deflate_set_dictionary)(PREFIX3(streamp) strm,
const unsigned char *dictionary, uInt dict_length) {
deflate_state *state = (deflate_state *)strm->state;
Expand All @@ -423,16 +393,8 @@ int Z_INTERNAL PREFIX(dfltcc_deflate_get_dictionary)(PREFIX3(streamp) strm, unsi
struct dfltcc_state *dfltcc_state = GET_DFLTCC_STATE(state);
struct dfltcc_param_v0 *param = &dfltcc_state->param;

if (dictionary) {
if (param->ho + param->hl <= HB_SIZE)
/* Circular history buffer does not wrap - copy one chunk */
memcpy(dictionary, state->window + param->ho, param->hl);
else {
/* Circular history buffer wraps - copy two chunks */
memcpy(dictionary, state->window + param->ho, HB_SIZE - param->ho);
memcpy(dictionary + HB_SIZE - param->ho, state->window, param->ho + param->hl - HB_SIZE);
}
}
if (dictionary)
get_history(param, state->window, dictionary);
if (dict_length)
*dict_length = param->hl;
return Z_OK;
Expand Down
43 changes: 43 additions & 0 deletions arch/s390/dfltcc_detail.h
Original file line number Diff line number Diff line change
Expand Up @@ -267,3 +267,46 @@ static inline void dfltcc_reset_state(struct dfltcc_state *dfltcc_state) {
static inline void dfltcc_copy_state(void *dst, const void *src, uInt size, uInt extension_size) {
memcpy(dst, src, ALIGN_UP(size, 8) + extension_size);
}

static inline void append_history(struct dfltcc_param_v0 *param, unsigned char *history,
const unsigned char *buf, uInt count) {
size_t offset;
size_t n;

/* Do not use more than 32K */
if (count > HB_SIZE) {
buf += count - HB_SIZE;
count = HB_SIZE;
}
offset = (param->ho + param->hl) % HB_SIZE;
if (offset + count <= HB_SIZE)
/* Circular history buffer does not wrap - copy one chunk */
memcpy(history + offset, buf, count);
else {
/* Circular history buffer wraps - copy two chunks */
n = HB_SIZE - offset;
memcpy(history + offset, buf, n);
memcpy(history, buf + n, count - n);
}
n = param->hl + count;
if (n <= HB_SIZE)
/* All history fits into buffer - no need to discard anything */
param->hl = n;
else {
/* History does not fit into buffer - discard extra bytes */
param->ho = (param->ho + (n - HB_SIZE)) % HB_SIZE;
param->hl = HB_SIZE;
}
}

static inline void get_history(struct dfltcc_param_v0 *param, const unsigned char *history,
unsigned char *buf) {
if (param->ho + param->hl <= HB_SIZE)
/* Circular history buffer does not wrap - copy one chunk */
memcpy(buf, history + param->ho, param->hl);
else {
/* Circular history buffer wraps - copy two chunks */
memcpy(buf, history + param->ho, HB_SIZE - param->ho);
memcpy(buf + HB_SIZE - param->ho, history, param->ho + param->hl - HB_SIZE);
}
}
67 changes: 59 additions & 8 deletions arch/s390/dfltcc_inflate.c
Original file line number Diff line number Diff line change
Expand Up @@ -39,10 +39,6 @@ int Z_INTERNAL PREFIX(dfltcc_can_inflate)(PREFIX3(streamp) strm) {
struct inflate_state *state = (struct inflate_state *)strm->state;
struct dfltcc_state *dfltcc_state = GET_DFLTCC_STATE(state);

/* Unsupported compression settings */
if (state->wbits != HB_BITS)
return 0;

/* Unsupported hardware */
return is_bit_set(dfltcc_state->af.fns, DFLTCC_XPND) && is_bit_set(dfltcc_state->af.fmts, DFLTCC_FMT0);
}
Expand Down Expand Up @@ -98,8 +94,6 @@ dfltcc_inflate_action Z_INTERNAL PREFIX(dfltcc_inflate)(PREFIX3(streamp) strm, i
/* Translate stream to parameter block */
param->cvt = state->flags ? CVT_CRC32 : CVT_ADLER32;
param->sbb = state->bits;
param->hl = state->whave; /* Software and hardware history formats match */
param->ho = (state->wnext - state->whave) & ((1 << HB_BITS) - 1);
if (param->hl)
param->nt = 0; /* Honor history for the first block */
param->cv = state->flags ? ZSWAP32(state->check) : state->check;
Expand All @@ -113,8 +107,6 @@ dfltcc_inflate_action Z_INTERNAL PREFIX(dfltcc_inflate)(PREFIX3(streamp) strm, i
strm->msg = oesc_msg(dfltcc_state->msg, param->oesc);
state->last = cc == DFLTCC_CC_OK;
state->bits = param->sbb;
state->whave = param->hl;
state->wnext = (param->ho + param->hl) & ((1 << HB_BITS) - 1);
strm->adler = state->check = state->flags ? ZSWAP32(param->cv) : param->cv;
if (cc == DFLTCC_CC_OP2_CORRUPT && param->oesc != 0) {
/* Report an error if stream is corrupted */
Expand All @@ -134,9 +126,33 @@ int Z_INTERNAL PREFIX(dfltcc_was_inflate_used)(PREFIX3(streamp) strm) {
return !param->nt;
}

/*
Rotates a circular buffer.
The implementation is based on https://cplusplus.com/reference/algorithm/rotate/
*/
static void rotate(unsigned char *start, unsigned char *pivot, unsigned char *end) {
unsigned char *p = pivot;
unsigned char tmp;

while (p != start) {
tmp = *start;
*start = *p;
*p = tmp;

start++;
p++;

if (p == end)
p = pivot;
else if (start == pivot)
pivot = p;
}
}

int Z_INTERNAL PREFIX(dfltcc_inflate_disable)(PREFIX3(streamp) strm) {
struct inflate_state *state = (struct inflate_state *)strm->state;
struct dfltcc_state *dfltcc_state = GET_DFLTCC_STATE(state);
struct dfltcc_param_v0 *param = &dfltcc_state->param;

if (!PREFIX(dfltcc_can_inflate)(strm))
return 0;
Expand All @@ -148,5 +164,40 @@ int Z_INTERNAL PREFIX(dfltcc_inflate_disable)(PREFIX3(streamp) strm) {
return 1;
/* DFLTCC was not used yet - decompress in software */
memset(&dfltcc_state->af, 0, sizeof(dfltcc_state->af));
/* Convert the window from the hardware to the software format */
rotate(state->window, state->window + param->ho, state->window + HB_SIZE);
state->whave = state->wnext = MIN(param->hl, state->wsize);
return 0;
}

/*
Preloading history.
*/
int Z_INTERNAL PREFIX(dfltcc_inflate_set_dictionary)(PREFIX3(streamp) strm,
const unsigned char *dictionary, uInt dict_length) {
struct inflate_state *state = (struct inflate_state *)strm->state;
struct dfltcc_state *dfltcc_state = GET_DFLTCC_STATE(state);
struct dfltcc_param_v0 *param = &dfltcc_state->param;

if (PREFIX(inflate_ensure_window)(state)) {
state->mode = MEM;
return Z_MEM_ERROR;
}

append_history(param, state->window, dictionary, dict_length);
state->havedict = 1;
return Z_OK;
}

int Z_INTERNAL PREFIX(dfltcc_inflate_get_dictionary)(PREFIX3(streamp) strm,
unsigned char *dictionary, uInt *dict_length) {
struct inflate_state *state = (struct inflate_state *)strm->state;
struct dfltcc_state *dfltcc_state = GET_DFLTCC_STATE(state);
struct dfltcc_param_v0 *param = &dfltcc_state->param;

if (dictionary && state->window)
get_history(param, state->window, dictionary);
if (dict_length)
*dict_length = param->hl;
return Z_OK;
}
16 changes: 16 additions & 0 deletions arch/s390/dfltcc_inflate.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,10 @@ typedef enum {
dfltcc_inflate_action Z_INTERNAL PREFIX(dfltcc_inflate)(PREFIX3(streamp) strm, int flush, int *ret);
int Z_INTERNAL PREFIX(dfltcc_was_inflate_used)(PREFIX3(streamp) strm);
int Z_INTERNAL PREFIX(dfltcc_inflate_disable)(PREFIX3(streamp) strm);
int Z_INTERNAL PREFIX(dfltcc_inflate_set_dictionary)(PREFIX3(streamp) strm,
const unsigned char *dictionary, uInt dict_length);
int Z_INTERNAL PREFIX(dfltcc_inflate_get_dictionary)(PREFIX3(streamp) strm,
unsigned char *dictionary, uInt* dict_length);

#define ZALLOC_INFLATE_STATE PREFIX(dfltcc_alloc_inflate_state)
#define ZCOPY_INFLATE_STATE PREFIX(dfltcc_copy_inflate_state)
Expand Down Expand Up @@ -51,4 +55,16 @@ int Z_INTERNAL PREFIX(dfltcc_inflate_disable)(PREFIX3(streamp) strm);
if (PREFIX(dfltcc_was_inflate_used)((strm))) return Z_STREAM_ERROR; \
} while (0)

#define INFLATE_SET_DICTIONARY_HOOK(strm, dict, dict_len) \
do { \
if (PREFIX(dfltcc_can_inflate)((strm))) \
return PREFIX(dfltcc_inflate_set_dictionary)((strm), (dict), (dict_len)); \
} while (0)

#define INFLATE_GET_DICTIONARY_HOOK(strm, dict, dict_len) \
do { \
if (PREFIX(dfltcc_can_inflate)((strm))) \
return PREFIX(dfltcc_inflate_get_dictionary)((strm), (dict), (dict_len)); \
} while (0)

#endif
7 changes: 5 additions & 2 deletions inflate.c
Original file line number Diff line number Diff line change
Expand Up @@ -1157,6 +1157,8 @@ int32_t Z_EXPORT PREFIX(inflateGetDictionary)(PREFIX3(stream) *strm, uint8_t *di
return Z_STREAM_ERROR;
state = (struct inflate_state *)strm->state;

INFLATE_GET_DICTIONARY_HOOK(strm, dictionary, dictLength); /* hook for IBM Z DFLTCC */

/* copy dictionary */
if (state->whave && dictionary != NULL) {
memcpy(dictionary, state->window + state->wnext, state->whave - state->wnext);
Expand Down Expand Up @@ -1186,6 +1188,8 @@ int32_t Z_EXPORT PREFIX(inflateSetDictionary)(PREFIX3(stream) *strm, const uint8
return Z_DATA_ERROR;
}

INFLATE_SET_DICTIONARY_HOOK(strm, dictionary, dictLength); /* hook for IBM Z DFLTCC */

/* copy dictionary to window using updatewindow(), which will amend the
existing dictionary if appropriate */
ret = updatewindow(strm, dictionary + dictLength, dictLength, 0);
Expand Down Expand Up @@ -1349,8 +1353,7 @@ int32_t Z_EXPORT PREFIX(inflateCopy)(PREFIX3(stream) *dest, PREFIX3(stream) *sou
}
copy->next = copy->codes + (state->next - state->codes);
if (window != NULL) {
wsize = 1U << state->wbits;
memcpy(window, state->window, wsize);
ZCOPY_WINDOW(window, state->window, 1U << state->wbits);
}
copy->window = window;
dest->state = (struct internal_state *)copy;
Expand Down
5 changes: 5 additions & 0 deletions inflate_p.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
# define ZCOPY_INFLATE_STATE(dst, src) memcpy(dst, src, sizeof(struct inflate_state))
/* Memory management for the window. Useful for allocation the aligned window. */
# define ZALLOC_WINDOW(strm, items, size) ZALLOC(strm, items, size)
# define ZCOPY_WINDOW(dest, src, n) memcpy(dest, src, n)
# define ZFREE_WINDOW(strm, addr) ZFREE(strm, addr)
/* Invoked at the end of inflateResetKeep(). Useful for initializing arch-specific extension blocks. */
# define INFLATE_RESET_KEEP_HOOK(strm) do {} while (0)
Expand All @@ -32,6 +33,10 @@
# define INFLATE_MARK_HOOK(strm) do {} while (0)
/* Invoked at the beginning of inflateSyncPoint(). Useful for performing arch-specific state checks. */
# define INFLATE_SYNC_POINT_HOOK(strm) do {} while (0)
/* Invoked at the beginning of inflateSetDictionary(). Useful for checking arch-specific window data. */
# define INFLATE_SET_DICTIONARY_HOOK(strm, dict, dict_len) do {} while (0)
/* Invoked at the beginning of inflateGetDictionary(). Useful for adjusting arch-specific window data. */
# define INFLATE_GET_DICTIONARY_HOOK(strm, dict, dict_len) do {} while (0)
#endif

/*
Expand Down
1 change: 1 addition & 0 deletions test/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,7 @@ else()
test_inflate_adler32.cc
test_large_buffers.cc
test_small_buffers.cc
test_small_window.cc
)

if(WITH_GZFILEOP)
Expand Down
Loading

0 comments on commit 3eab317

Please sign in to comment.