0449febc4e
So that we can also reserve space for things that will be added in several steps, such as CTF structs, where we first add a struct for the name, size, nr_members, then several ones for the members. Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
134 lines
2.7 KiB
C
134 lines
2.7 KiB
C
/*
|
|
Copyright (C) 2008 Arnaldo Carvalho de Melo <acme@redhat.com>
|
|
|
|
Grow only buffer, add entries but never delete
|
|
|
|
This program is free software; you can redistribute it and/or modify it
|
|
under the terms of version 2 of the GNU General Public License as
|
|
published by the Free Software Foundation.
|
|
*/
|
|
|
|
#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 *self)
|
|
{
|
|
self->entries = NULL;
|
|
self->nr_entries = self->allocated_size = 0;
|
|
/* 0 == NULL */
|
|
self->index = 1;
|
|
}
|
|
|
|
struct gobuffer *gobuffer__new(void)
|
|
{
|
|
struct gobuffer *self = malloc(sizeof(*self));
|
|
|
|
if (self != NULL)
|
|
gobuffer__init(self);
|
|
|
|
return self;
|
|
}
|
|
|
|
void __gobuffer__delete(struct gobuffer *self)
|
|
{
|
|
free(self->entries);
|
|
}
|
|
|
|
void gobuffer__delete(struct gobuffer *self)
|
|
{
|
|
__gobuffer__delete(self);
|
|
free(self);
|
|
}
|
|
|
|
void *gobuffer__ptr(const struct gobuffer *self, unsigned int s)
|
|
{
|
|
return s ? self->entries + s : NULL;
|
|
}
|
|
|
|
int gobuffer__allocate(struct gobuffer *self, unsigned int len)
|
|
{
|
|
const unsigned int rc = self->index;
|
|
const unsigned int index = self->index + len;
|
|
|
|
if (index >= self->allocated_size) {
|
|
const unsigned int allocated_size = (self->allocated_size +
|
|
GOBUFFER__BCHUNK);
|
|
char *entries = realloc(self->entries, allocated_size);
|
|
|
|
if (entries == NULL)
|
|
return -ENOMEM;
|
|
|
|
self->allocated_size = allocated_size;
|
|
self->entries = entries;
|
|
}
|
|
|
|
self->index = index;
|
|
return rc;
|
|
}
|
|
|
|
int gobuffer__add(struct gobuffer *self, const void *s, unsigned int len)
|
|
{
|
|
const int rc = gobuffer__allocate(self, len);
|
|
|
|
if (rc >= 0) {
|
|
++self->nr_entries;
|
|
memcpy(self->entries + rc, s, len);
|
|
}
|
|
return rc;
|
|
}
|
|
|
|
const void *gobuffer__compress(struct gobuffer *self, unsigned int *size)
|
|
{
|
|
z_stream z = {
|
|
.zalloc = Z_NULL,
|
|
.zfree = Z_NULL,
|
|
.opaque = Z_NULL,
|
|
.avail_in = gobuffer__size(self),
|
|
.next_in = (Bytef *)gobuffer__entries(self),
|
|
};
|
|
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;
|
|
}
|