2 * Copyright (c) 2011 Pawel Jakub Dawidek <pawel@dawidek.net>
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27 #include <sys/cdefs.h>
28 __FBSDID("$FreeBSD$");
30 #include <sys/endian.h>
41 #include "hast_compression.h"
44 allzeros(const void *data, size_t size)
46 const uint64_t *p = data;
50 PJDLOG_ASSERT((size % sizeof(*p)) == 0);
53 * This is the fastest method I found for checking if the given
54 * buffer contain all zeros.
55 * Because inside the loop we don't check at every step, we would
56 * get an answer only after walking through entire buffer.
57 * To return early if the buffer doesn't contain all zeros, we probe
58 * 8 bytes at the beginning, in the middle and at the end of the buffer
62 size >>= 3; /* divide by 8 */
63 if ((p[0] | p[size >> 1] | p[size - 1]) != 0)
66 for (i = 0; i < size; i++)
72 hast_hole_compress(const unsigned char *data, size_t *sizep)
77 if (!allzeros(data, *sizep))
80 newbuf = malloc(sizeof(size));
82 pjdlog_warning("Unable to compress (no memory: %zu).",
86 size = htole32((uint32_t)*sizep);
87 bcopy(&size, newbuf, sizeof(size));
88 *sizep = sizeof(size);
94 hast_hole_decompress(const unsigned char *data, size_t *sizep)
99 if (*sizep != sizeof(size)) {
100 pjdlog_error("Unable to decompress (invalid size: %zu).",
105 bcopy(data, &size, sizeof(size));
106 size = le32toh(size);
108 newbuf = malloc(size);
109 if (newbuf == NULL) {
110 pjdlog_error("Unable to decompress (no memory: %zu).",
120 /* Minimum block size to try to compress. */
121 #define HAST_LZF_COMPRESS_MIN 1024
124 hast_lzf_compress(const unsigned char *data, size_t *sizep)
126 unsigned char *newbuf;
132 if (origsize <= HAST_LZF_COMPRESS_MIN)
135 newsize = sizeof(origsize) + origsize - HAST_LZF_COMPRESS_MIN;
136 newbuf = malloc(newsize);
137 if (newbuf == NULL) {
138 pjdlog_warning("Unable to compress (no memory: %zu).",
142 newsize = lzf_compress(data, *sizep, newbuf + sizeof(origsize),
143 newsize - sizeof(origsize));
148 origsize = htole32(origsize);
149 bcopy(&origsize, newbuf, sizeof(origsize));
151 *sizep = sizeof(origsize) + newsize;
156 hast_lzf_decompress(const unsigned char *data, size_t *sizep)
158 unsigned char *newbuf;
162 PJDLOG_ASSERT(*sizep > sizeof(origsize));
164 bcopy(data, &origsize, sizeof(origsize));
165 origsize = le32toh(origsize);
166 PJDLOG_ASSERT(origsize > HAST_LZF_COMPRESS_MIN);
168 newbuf = malloc(origsize);
169 if (newbuf == NULL) {
170 pjdlog_error("Unable to decompress (no memory: %zu).",
174 newsize = lzf_decompress(data + sizeof(origsize),
175 *sizep - sizeof(origsize), newbuf, origsize);
178 pjdlog_error("Unable to decompress.");
181 PJDLOG_ASSERT(newsize == origsize);
188 compression_name(int num)
192 case HAST_COMPRESSION_NONE:
194 case HAST_COMPRESSION_HOLE:
196 case HAST_COMPRESSION_LZF:
203 compression_send(const struct hast_resource *res, struct nv *nv, void **datap,
204 size_t *sizep, bool *freedatap)
206 unsigned char *newbuf;
211 compression = res->hr_compression;
213 switch (compression) {
214 case HAST_COMPRESSION_NONE:
216 case HAST_COMPRESSION_HOLE:
217 newbuf = hast_hole_compress(*datap, &size);
219 case HAST_COMPRESSION_LZF:
220 /* Try 'hole' compression first. */
221 newbuf = hast_hole_compress(*datap, &size);
223 compression = HAST_COMPRESSION_HOLE;
225 newbuf = hast_lzf_compress(*datap, &size);
228 PJDLOG_ABORT("Invalid compression: %d.", res->hr_compression);
231 if (newbuf == NULL) {
232 /* Unable to compress the data. */
235 nv_add_string(nv, compression_name(compression), "compression");
236 if (nv_error(nv) != 0) {
238 errno = nv_error(nv);
251 compression_recv(const struct hast_resource *res __unused, struct nv *nv,
252 void **datap, size_t *sizep, bool *freedatap)
254 unsigned char *newbuf;
258 algo = nv_get_string(nv, "compression");
260 return (0); /* No compression. */
265 if (strcmp(algo, "hole") == 0)
266 newbuf = hast_hole_decompress(*datap, &size);
267 else if (strcmp(algo, "lzf") == 0)
268 newbuf = hast_lzf_decompress(*datap, &size);
270 pjdlog_error("Unknown compression algorithm '%s'.", algo);
271 return (-1); /* Unknown compression algorithm. */