/* SPDX-License-Identifier: GPL-2.0-only Copyright (C) 2008 Arnaldo Carvalho de Melo Grow only buffer, add entries but never delete */ #include "gobuffer.h" #include #include #include #include #include #include #include #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)); } } 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; }