-
Notifications
You must be signed in to change notification settings - Fork 51
/
gobuffer.c
151 lines (121 loc) · 2.95 KB
/
gobuffer.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
/*
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;
}