| /* |
| SPDX-License-Identifier: GPL-2.0-only |
| |
| Copyright (C) 2008 Arnaldo Carvalho de Melo <[email protected]> |
| |
| Grow only buffer, add entries but never delete |
| */ |
| |
| #include "gobuffer.h" |
| |
| #include <search.h> |
| #include <stdint.h> |
| #include <stdlib.h> |
| #include <stdio.h> |
| #include <string.h> |
| #include <zlib.h> |
| #include <errno.h> |
| |
| #include "dutil.h" |
| |
| #define GOBUFFER__BCHUNK (8 * 1024) |
| #define GOBUFFER__ZCHUNK (8 * 1024) |
| |
| void gobuffer__init(struct gobuffer *gb) |
| { |
| gb->entries = NULL; |
| gb->nr_entries = gb->allocated_size = 0; |
| /* 0 == NULL */ |
| gb->index = 1; |
| } |
| |
| struct gobuffer *gobuffer__new(void) |
| { |
| struct gobuffer *gb = malloc(sizeof(*gb)); |
| |
| if (gb != NULL) |
| gobuffer__init(gb); |
| |
| return gb; |
| } |
| |
| void __gobuffer__delete(struct gobuffer *gb) |
| { |
| if (gb == NULL) |
| return; |
| |
| zfree(&gb->entries); |
| } |
| |
| void gobuffer__delete(struct gobuffer *gb) |
| { |
| __gobuffer__delete(gb); |
| free(gb); |
| } |
| |
| void *gobuffer__ptr(const struct gobuffer *gb, unsigned int s) |
| { |
| return s ? gb->entries + s : NULL; |
| } |
| |
| int gobuffer__allocate(struct gobuffer *gb, unsigned int len) |
| { |
| const unsigned int rc = gb->index; |
| const unsigned int index = gb->index + len; |
| |
| if (index >= gb->allocated_size) { |
| unsigned int allocated_size = (gb->allocated_size + |
| GOBUFFER__BCHUNK); |
| if (allocated_size < index) |
| allocated_size = index + GOBUFFER__BCHUNK; |
| char *entries = realloc(gb->entries, allocated_size); |
| |
| if (entries == NULL) |
| return -ENOMEM; |
| |
| gb->allocated_size = allocated_size; |
| gb->entries = entries; |
| } |
| |
| gb->index = index; |
| return rc; |
| } |
| |
| int gobuffer__add(struct gobuffer *gb, const void *s, unsigned int len) |
| { |
| const int rc = gobuffer__allocate(gb, len); |
| |
| if (rc >= 0) { |
| ++gb->nr_entries; |
| memcpy(gb->entries + rc, s, len); |
| } |
| return rc; |
| } |
| |
| void gobuffer__copy(const struct gobuffer *gb, void *dest) |
| { |
| if (gb->entries) { |
| memcpy(dest, gb->entries, gobuffer__size(gb)); |
| } else { |
| /* gobuffer__size will be 0 or 1. */ |
| memcpy(dest, "", gobuffer__size(gb)); |
| } |
| } |
| |
| void gobuffer__sort(struct gobuffer *gb, unsigned int size, int (*compar)(const void *, const void *)) |
| { |
| qsort(gb->entries, gb->nr_entries, size, compar); |
| } |
| |
| const void *gobuffer__compress(struct gobuffer *gb, unsigned int *size) |
| { |
| z_stream z = { |
| .zalloc = Z_NULL, |
| .zfree = Z_NULL, |
| .opaque = Z_NULL, |
| .avail_in = gobuffer__size(gb), |
| .next_in = (Bytef *)(gobuffer__entries(gb) ? : ""), |
| }; |
| void *bf = NULL; |
| unsigned int bf_size = 0; |
| |
| if (deflateInit(&z, Z_BEST_COMPRESSION) != Z_OK) |
| goto out_free; |
| |
| do { |
| const unsigned int new_bf_size = bf_size + GOBUFFER__ZCHUNK; |
| void *nbf = realloc(bf, new_bf_size); |
| |
| if (nbf == NULL) |
| goto out_close_and_free; |
| |
| bf = nbf; |
| z.avail_out = GOBUFFER__ZCHUNK; |
| z.next_out = (Bytef *)bf + bf_size; |
| bf_size = new_bf_size; |
| if (deflate(&z, Z_FINISH) == Z_STREAM_ERROR) |
| goto out_close_and_free; |
| } while (z.avail_out == 0); |
| |
| deflateEnd(&z); |
| *size = bf_size - z.avail_out; |
| out: |
| return bf; |
| |
| out_close_and_free: |
| deflateEnd(&z); |
| out_free: |
| free(bf); |
| bf = NULL; |
| goto out; |
| } |