]> git.ipfire.org Git - pakfire.git/commitdiff
compress: Refactor transparent Zstandard compression
authorMichael Tremer <michael.tremer@ipfire.org>
Wed, 29 Jan 2025 11:46:39 +0000 (11:46 +0000)
committerMichael Tremer <michael.tremer@ipfire.org>
Wed, 29 Jan 2025 11:46:39 +0000 (11:46 +0000)
Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
src/pakfire/compress.c

index 0ccbc1030504202213791e13d99e7c66d43aad99..c02308958464425077d7f65146bb5f8fb2883e8b 100644 (file)
@@ -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;
 }