From: Michael Tremer Date: Wed, 29 Jan 2025 11:46:39 +0000 (+0000) Subject: compress: Refactor transparent Zstandard compression X-Git-Tag: 0.9.30~306 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=2b02a4f38bcc89b6bf2a2863061e5ad4cc277167;p=pakfire.git compress: Refactor transparent Zstandard compression Signed-off-by: Michael Tremer --- diff --git a/src/pakfire/compress.c b/src/pakfire/compress.c index 0ccbc103..c0230895 100644 --- a/src/pakfire/compress.c +++ b/src/pakfire/compress.c @@ -327,8 +327,18 @@ struct zstd_cookie { uint8_t buffer[BUFFER_SIZE]; }; +static void zstd_free(struct zstd_cookie* cookie) { + if (cookie->cstream) + ZSTD_freeCStream(cookie->cstream); + if (cookie->dstream) + ZSTD_freeDStream(cookie->dstream); + free(cookie); +} + static ssize_t zstd_read(void* data, char* buffer, size_t size) { - struct zstd_cookie* cookie = (struct zstd_cookie*)data; + struct zstd_cookie* cookie = data; + int r; + if (!cookie) return -1; @@ -339,38 +349,34 @@ static ssize_t zstd_read(void* data, char* buffer, size_t size) { if (cookie->done) return 0; - size_t r = 0; - // Configure output buffer - cookie->out.dst = buffer; - cookie->out.pos = 0; + cookie->out.dst = buffer; cookie->out.size = size; + cookie->out.pos = 0; - while (1) { - if (!feof(cookie->f) && (cookie->in.pos == cookie->in.size)) { - cookie->in.pos = 0; + while (cookie->out.pos < cookie->out.size) { + if (cookie->in.pos >= cookie->in.size) { cookie->in.size = fread(cookie->buffer, 1, sizeof(cookie->buffer), cookie->f); - } - if (r || cookie->in.size) - r = ZSTD_decompressStream(cookie->dstream, &cookie->out, &cookie->in); + // EOF? + if (!cookie->in.size) + return 0; - if (r == 0 && feof(cookie->f)) { - cookie->done = 1; - return cookie->out.pos; + cookie->in.pos = 0; } + r = ZSTD_decompressStream(cookie->dstream, &cookie->out, &cookie->in); if (ZSTD_isError(r)) return -1; - - // Buffer full - if (cookie->out.pos == size) - return size; } + + return cookie->out.pos; } static ssize_t zstd_write(void* data, const char* buffer, size_t size) { - struct zstd_cookie* cookie = (struct zstd_cookie*)data; + struct zstd_cookie* cookie = data; + int r; + if (!cookie) return -1; @@ -378,69 +384,84 @@ static ssize_t zstd_write(void* data, const char* buffer, size_t size) { if (cookie->mode == 'r') return -1; - // Return nothing when there is no input - if (size == 0) - return 0; - // Configure input buffer - cookie->in.src = buffer; - cookie->in.pos = 0; + cookie->in.src = buffer; cookie->in.size = size; + cookie->in.pos = 0; - while (1) { - cookie->out.pos = 0; + while (cookie->in.pos < cookie->in.size) { + cookie->out.dst = cookie->buffer; + cookie->out.size = sizeof(cookie->buffer); + cookie->out.pos = 0; - size_t r = ZSTD_compressStream(cookie->cstream, &cookie->out, &cookie->in); + r = ZSTD_compressStream(cookie->cstream, &cookie->out, &cookie->in); if (ZSTD_isError(r)) return -1; - if (cookie->out.pos > 0) { - size_t bytes_written = fwrite(cookie->buffer, 1, cookie->out.pos, cookie->f); - - if (bytes_written != cookie->out.pos) - return -1; - } - - // Return when all input has been written - if (cookie->in.pos == size) - return size; + r = fwrite(cookie->buffer, 1, cookie->out.pos, cookie->f); + if (r < (ssize_t)cookie->out.pos) + return -1; } + + return size; } -static int zstd_close(void* data) { - struct zstd_cookie* cookie = (struct zstd_cookie*)data; +static int zstd_flush(void* data) { + struct zstd_cookie* cookie = data; + int r; + + // Fail if were given no cookie if (!cookie) - return -1; + return -EINVAL; + // Reset the input buffer + cookie->in.src = NULL; + cookie->in.size = 0; + cookie->in.pos = 0; + + // In write mode, we will have to flush all buffers if (cookie->mode == 'w') { - while (1) { + for (;;) { // Reset output buffer + cookie->out.dst = cookie->buffer; + cookie->out.size = sizeof(cookie->buffer); cookie->out.pos = 0; - size_t r = ZSTD_endStream(cookie->cstream, &cookie->out); + r = ZSTD_endStream(cookie->cstream, &cookie->out); if (ZSTD_isError(r)) return -1; - if (cookie->out.pos > 0) { - size_t bytes_written = fwrite(cookie->buffer, 1, cookie->out.pos, cookie->f); - - if (bytes_written != cookie->out.pos) - return -1; - } - + // If the buffer is empty we are done if (r == 0) break; + + // Otherwise we write the buffer to the file + r = fwrite(cookie->buffer, 1, cookie->out.pos, cookie->f); + if (r < (ssize_t)cookie->out.pos) + return -1; } } - int r = fclose(cookie->f); + return 0; +} + +static int zstd_close(void* data) { + struct zstd_cookie* cookie = data; + int r; + + // Fail if were given no cookie + if (!cookie) + return -EINVAL; + + // In write mode, we will have to flush all buffers + if (cookie->mode == 'w') + zstd_flush(cookie); + + // Close the underlying file handle + r = fclose(cookie->f); // Free everything - if (cookie->cstream) - ZSTD_freeCStream(cookie->cstream); - if (cookie->dstream) - ZSTD_freeDStream(cookie->dstream); - free(cookie); + zstd_free(cookie); return r; } @@ -453,24 +474,32 @@ static cookie_io_functions_t zstd_functions = { }; FILE* pakfire_zstdfopen(FILE* f, const char* mode) { + struct zstd_cookie* cookie = NULL; + int r; + + // Check for a valid file handle if (!f) { errno = EBADFD; return NULL; } + // Check for some mode if (!mode) { errno = EINVAL; return NULL; } - struct zstd_cookie* cookie = calloc(1, sizeof(*cookie)); + // Allocate a new cookie + cookie = calloc(1, sizeof(*cookie)); if (!cookie) - return NULL; + goto ERROR; + // Store the file handle cookie->f = f; + + // Store the mode cookie->mode = *mode; - size_t r; switch (cookie->mode) { case 'r': // Allocate stream @@ -512,11 +541,8 @@ FILE* pakfire_zstdfopen(FILE* f, const char* mode) { return fopencookie(cookie, mode, zstd_functions); ERROR: - if (cookie->cstream) - ZSTD_freeCStream(cookie->cstream); - if (cookie->dstream) - ZSTD_freeDStream(cookie->dstream); - free(cookie); + if (cookie) + zstd_free(cookie); return NULL; }