Skip to content
Closed
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
10 changes: 9 additions & 1 deletion gzwrite.c
Original file line number Diff line number Diff line change
Expand Up @@ -242,9 +242,17 @@ local z_size_t gz_write(gz_statep state, voidpc buf, z_size_t len) {
n -= state->strm.avail_in;
state->x.pos += n;
len -= n;
if (ret == -1)
if (ret == -1) {
/*
* The bytes not reported as consumed remain owned by the
* caller. Do not keep their address after returning.
*/
state->strm.avail_in = 0;
state->strm.next_in = Z_NULL;
return state->again ? put - len : 0;
}
} while (len);
state->strm.next_in = Z_NULL;
}

/* input was all buffered or compressed */
Expand Down
6 changes: 6 additions & 0 deletions test/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,12 @@ if(ZLIB_BUILD_STATIC)
$<$<BOOL:${HAVE___ATTR__VIS_HIDDEN}>:HAVE_HIDDEN>)
add_test(NAME zlib_examplestatic COMMAND zlib_examplestatic)

if(UNIX)
add_executable(gznonblock gznonblock.c)
target_link_libraries(gznonblock ZLIB::ZLIBSTATIC)
add_test(NAME zlib_gznonblock COMMAND gznonblock)
endif(UNIX)

add_executable(zlib_minigzipstatic minigzip.c)
target_link_libraries(zlib_minigzipstatic ZLIB::ZLIBSTATIC)
set_target_properties(zlib_minigzipstatic
Expand Down
90 changes: 90 additions & 0 deletions test/gznonblock.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
/* gznonblock.c -- test non-blocking gzip writes
* Copyright (C) 2026 Mark Adler
* For conditions of distribution and use, see copyright notice in zlib.h
*/

#include "zlib.h"
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#define INPUT_SIZE (1024 * 1024)

int main(void) {
int pipefd[2];
unsigned char fill[4096];
unsigned char *input;
unsigned seed;
size_t i;
gzFile file;
int put;

if (pipe(pipefd) == -1 ||
fcntl(pipefd[1], F_SETFL,
fcntl(pipefd[1], F_GETFL) | O_NONBLOCK) == -1) {
perror("pipe");
return 1;
}

memset(fill, 0xa5, sizeof(fill));
while (write(pipefd[1], fill, sizeof(fill)) > 0)
;
if (errno != EAGAIN && errno != EWOULDBLOCK) {
perror("fill pipe");
return 1;
}

/*
* Use incompressible input larger than the gzip buffers so that the full
* pipe stalls gzwrite() before it consumes the caller's entire buffer.
*/
input = (unsigned char *)malloc(INPUT_SIZE);
if (input == NULL) {
fprintf(stderr, "out of memory\n");
return 1;
}
seed = 0x12345678U;
for (i = 0; i < INPUT_SIZE; i++) {
seed ^= seed << 13;
seed ^= seed >> 17;
seed ^= seed << 5;
input[i] = (unsigned char)seed;
}

file = gzdopen(pipefd[1], "wbN");
if (file == NULL) {
fprintf(stderr, "gzdopen failed\n");
return 1;
}

put = gzwrite(file, input, INPUT_SIZE);
free(input);
if (put <= 0 || put >= INPUT_SIZE) {
fprintf(stderr, "gzwrite did not make partial progress: %d\n", put);
return 1;
}

/*
* gzwrite() reported the remaining input as unconsumed, so the caller was
* allowed to release it. A following gzip operation must not access it.
*/
put = gzprintf(file, "%s", "X");
if (put != 1) {
fprintf(stderr, "gzprintf failed after partial gzwrite: %d\n", put);
return 1;
}

if (fcntl(pipefd[0], F_SETFL,
fcntl(pipefd[0], F_GETFL) | O_NONBLOCK) == -1) {
perror("read pipe");
return 1;
}
while (read(pipefd[0], fill, sizeof(fill)) > 0)
;
(void)gzclose(file);
close(pipefd[0]);
return 0;
}