]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sbin/hastd/hast_compression.c
bhyvectl(8): Normalize the man page date
[FreeBSD/FreeBSD.git] / sbin / hastd / hast_compression.c
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3  *
4  * Copyright (c) 2011 Pawel Jakub Dawidek <pawel@dawidek.net>
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
17  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
20  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26  * SUCH DAMAGE.
27  */
28
29 #include <sys/cdefs.h>
30 __FBSDID("$FreeBSD$");
31
32 #include <sys/endian.h>
33
34 #include <errno.h>
35 #include <string.h>
36 #include <strings.h>
37
38 #include <hast.h>
39 #include <lzf.h>
40 #include <nv.h>
41 #include <pjdlog.h>
42
43 #include "hast_compression.h"
44
45 static bool
46 allzeros(const void *data, size_t size)
47 {
48         const uint64_t *p = data;
49         unsigned int i;
50         uint64_t v;
51
52         PJDLOG_ASSERT((size % sizeof(*p)) == 0);
53
54         /*
55          * This is the fastest method I found for checking if the given
56          * buffer contain all zeros.
57          * Because inside the loop we don't check at every step, we would
58          * get an answer only after walking through entire buffer.
59          * To return early if the buffer doesn't contain all zeros, we probe
60          * 8 bytes at the beginning, in the middle and at the end of the buffer
61          * first.
62          */
63
64         size >>= 3;     /* divide by 8 */
65         if ((p[0] | p[size >> 1] | p[size - 1]) != 0)
66                 return (false);
67         v = 0;
68         for (i = 0; i < size; i++)
69                 v |= *p++;
70         return (v == 0);
71 }
72
73 static void *
74 hast_hole_compress(const unsigned char *data, size_t *sizep)
75 {
76         uint32_t size;
77         void *newbuf;
78
79         if (!allzeros(data, *sizep))
80                 return (NULL);
81
82         newbuf = malloc(sizeof(size));
83         if (newbuf == NULL) {
84                 pjdlog_warning("Unable to compress (no memory: %zu).",
85                     (size_t)*sizep);
86                 return (NULL);
87         }
88         size = htole32((uint32_t)*sizep);
89         bcopy(&size, newbuf, sizeof(size));
90         *sizep = sizeof(size);
91
92         return (newbuf);
93 }
94
95 static void *
96 hast_hole_decompress(const unsigned char *data, size_t *sizep)
97 {
98         uint32_t size;
99         void *newbuf;
100
101         if (*sizep != sizeof(size)) {
102                 pjdlog_error("Unable to decompress (invalid size: %zu).",
103                     *sizep);
104                 return (NULL);
105         }
106
107         bcopy(data, &size, sizeof(size));
108         size = le32toh(size);
109
110         newbuf = malloc(size);
111         if (newbuf == NULL) {
112                 pjdlog_error("Unable to decompress (no memory: %zu).",
113                     (size_t)size);
114                 return (NULL);
115         }
116         bzero(newbuf, size);
117         *sizep = size;
118
119         return (newbuf);
120 }
121
122 /* Minimum block size to try to compress. */
123 #define HAST_LZF_COMPRESS_MIN   1024
124
125 static void *
126 hast_lzf_compress(const unsigned char *data, size_t *sizep)
127 {
128         unsigned char *newbuf;
129         uint32_t origsize;
130         size_t newsize;
131
132         origsize = *sizep;
133
134         if (origsize <= HAST_LZF_COMPRESS_MIN)
135                 return (NULL);
136
137         newsize = sizeof(origsize) + origsize - HAST_LZF_COMPRESS_MIN;
138         newbuf = malloc(newsize);
139         if (newbuf == NULL) {
140                 pjdlog_warning("Unable to compress (no memory: %zu).",
141                     newsize);
142                 return (NULL);
143         }
144         newsize = lzf_compress(data, *sizep, newbuf + sizeof(origsize),
145             newsize - sizeof(origsize));
146         if (newsize == 0) {
147                 free(newbuf);
148                 return (NULL);
149         }
150         origsize = htole32(origsize);
151         bcopy(&origsize, newbuf, sizeof(origsize));
152
153         *sizep = sizeof(origsize) + newsize;
154         return (newbuf);
155 }
156
157 static void *
158 hast_lzf_decompress(const unsigned char *data, size_t *sizep)
159 {
160         unsigned char *newbuf;
161         uint32_t origsize;
162         size_t newsize;
163
164         PJDLOG_ASSERT(*sizep > sizeof(origsize));
165
166         bcopy(data, &origsize, sizeof(origsize));
167         origsize = le32toh(origsize);
168         PJDLOG_ASSERT(origsize > HAST_LZF_COMPRESS_MIN);
169
170         newbuf = malloc(origsize);
171         if (newbuf == NULL) {
172                 pjdlog_error("Unable to decompress (no memory: %zu).",
173                     (size_t)origsize);
174                 return (NULL);
175         }
176         newsize = lzf_decompress(data + sizeof(origsize),
177             *sizep - sizeof(origsize), newbuf, origsize);
178         if (newsize == 0) {
179                 free(newbuf);
180                 pjdlog_error("Unable to decompress.");
181                 return (NULL);
182         }
183         PJDLOG_ASSERT(newsize == origsize);
184
185         *sizep = newsize;
186         return (newbuf);
187 }
188
189 const char *
190 compression_name(int num)
191 {
192
193         switch (num) {
194         case HAST_COMPRESSION_NONE:
195                 return ("none");
196         case HAST_COMPRESSION_HOLE:
197                 return ("hole");
198         case HAST_COMPRESSION_LZF:
199                 return ("lzf");
200         }
201         return ("unknown");
202 }
203
204 int
205 compression_send(const struct hast_resource *res, struct nv *nv, void **datap,
206     size_t *sizep, bool *freedatap)
207 {
208         unsigned char *newbuf;
209         int compression;
210         size_t size;
211
212         size = *sizep;
213         compression = res->hr_compression;
214
215         switch (compression) {
216         case HAST_COMPRESSION_NONE:
217                 return (0);
218         case HAST_COMPRESSION_HOLE:
219                 newbuf = hast_hole_compress(*datap, &size);
220                 break;
221         case HAST_COMPRESSION_LZF:
222                 /* Try 'hole' compression first. */
223                 newbuf = hast_hole_compress(*datap, &size);
224                 if (newbuf != NULL)
225                         compression = HAST_COMPRESSION_HOLE;
226                 else
227                         newbuf = hast_lzf_compress(*datap, &size);
228                 break;
229         default:
230                 PJDLOG_ABORT("Invalid compression: %d.", res->hr_compression);
231         }
232
233         if (newbuf == NULL) {
234                 /* Unable to compress the data. */
235                 return (0);
236         }
237         nv_add_string(nv, compression_name(compression), "compression");
238         if (nv_error(nv) != 0) {
239                 free(newbuf);
240                 errno = nv_error(nv);
241                 return (-1);
242         }
243         if (*freedatap)
244                 free(*datap);
245         *freedatap = true;
246         *datap = newbuf;
247         *sizep = size;
248
249         return (0);
250 }
251
252 int
253 compression_recv(const struct hast_resource *res __unused, struct nv *nv,
254     void **datap, size_t *sizep, bool *freedatap)
255 {
256         unsigned char *newbuf;
257         const char *algo;
258         size_t size;
259
260         algo = nv_get_string(nv, "compression");
261         if (algo == NULL)
262                 return (0);     /* No compression. */
263
264         newbuf = NULL;
265         size = *sizep;
266
267         if (strcmp(algo, "hole") == 0)
268                 newbuf = hast_hole_decompress(*datap, &size);
269         else if (strcmp(algo, "lzf") == 0)
270                 newbuf = hast_lzf_decompress(*datap, &size);
271         else {
272                 pjdlog_error("Unknown compression algorithm '%s'.", algo);
273                 return (-1);    /* Unknown compression algorithm. */
274         }
275
276         if (newbuf == NULL)
277                 return (-1);
278         if (*freedatap)
279                 free(*datap);
280         *freedatap = true;
281         *datap = newbuf;
282         *sizep = size;
283
284         return (0);
285 }