1219354Spjd/*- 2219354Spjd * Copyright (c) 2011 Pawel Jakub Dawidek <pawel@dawidek.net> 3219354Spjd * All rights reserved. 4219354Spjd * 5219354Spjd * Redistribution and use in source and binary forms, with or without 6219354Spjd * modification, are permitted provided that the following conditions 7219354Spjd * are met: 8219354Spjd * 1. Redistributions of source code must retain the above copyright 9219354Spjd * notice, this list of conditions and the following disclaimer. 10219354Spjd * 2. Redistributions in binary form must reproduce the above copyright 11219354Spjd * notice, this list of conditions and the following disclaimer in the 12219354Spjd * documentation and/or other materials provided with the distribution. 13219354Spjd * 14219354Spjd * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND 15219354Spjd * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16219354Spjd * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17219354Spjd * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE 18219354Spjd * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19219354Spjd * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20219354Spjd * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21219354Spjd * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22219354Spjd * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23219354Spjd * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24219354Spjd * SUCH DAMAGE. 25219354Spjd */ 26219354Spjd 27219354Spjd#include <sys/cdefs.h> 28219354Spjd__FBSDID("$FreeBSD$"); 29219354Spjd 30219354Spjd#include <sys/endian.h> 31219354Spjd 32219354Spjd#include <errno.h> 33219354Spjd#include <string.h> 34219354Spjd#include <strings.h> 35219354Spjd 36219354Spjd#include <hast.h> 37219354Spjd#include <lzf.h> 38219354Spjd#include <nv.h> 39219354Spjd#include <pjdlog.h> 40219354Spjd 41219354Spjd#include "hast_compression.h" 42219354Spjd 43219354Spjdstatic bool 44219354Spjdallzeros(const void *data, size_t size) 45219354Spjd{ 46219354Spjd const uint64_t *p = data; 47219354Spjd unsigned int i; 48219354Spjd uint64_t v; 49219354Spjd 50219354Spjd PJDLOG_ASSERT((size % sizeof(*p)) == 0); 51219354Spjd 52219354Spjd /* 53219354Spjd * This is the fastest method I found for checking if the given 54219354Spjd * buffer contain all zeros. 55219354Spjd * Because inside the loop we don't check at every step, we would 56219354Spjd * get an answer only after walking through entire buffer. 57219354Spjd * To return early if the buffer doesn't contain all zeros, we probe 58231017Strociny * 8 bytes at the beginning, in the middle and at the end of the buffer 59219354Spjd * first. 60219354Spjd */ 61219354Spjd 62219354Spjd size >>= 3; /* divide by 8 */ 63219354Spjd if ((p[0] | p[size >> 1] | p[size - 1]) != 0) 64219354Spjd return (false); 65219354Spjd v = 0; 66219354Spjd for (i = 0; i < size; i++) 67219354Spjd v |= *p++; 68219354Spjd return (v == 0); 69219354Spjd} 70219354Spjd 71219354Spjdstatic void * 72219354Spjdhast_hole_compress(const unsigned char *data, size_t *sizep) 73219354Spjd{ 74219354Spjd uint32_t size; 75219354Spjd void *newbuf; 76219354Spjd 77219354Spjd if (!allzeros(data, *sizep)) 78219354Spjd return (NULL); 79219354Spjd 80219354Spjd newbuf = malloc(sizeof(size)); 81219354Spjd if (newbuf == NULL) { 82219354Spjd pjdlog_warning("Unable to compress (no memory: %zu).", 83219354Spjd (size_t)*sizep); 84219354Spjd return (NULL); 85219354Spjd } 86219354Spjd size = htole32((uint32_t)*sizep); 87219354Spjd bcopy(&size, newbuf, sizeof(size)); 88219354Spjd *sizep = sizeof(size); 89219354Spjd 90219354Spjd return (newbuf); 91219354Spjd} 92219354Spjd 93219354Spjdstatic void * 94219354Spjdhast_hole_decompress(const unsigned char *data, size_t *sizep) 95219354Spjd{ 96219354Spjd uint32_t size; 97219354Spjd void *newbuf; 98219354Spjd 99219354Spjd if (*sizep != sizeof(size)) { 100219354Spjd pjdlog_error("Unable to decompress (invalid size: %zu).", 101219354Spjd *sizep); 102219354Spjd return (NULL); 103219354Spjd } 104219354Spjd 105219354Spjd bcopy(data, &size, sizeof(size)); 106219354Spjd size = le32toh(size); 107219354Spjd 108219354Spjd newbuf = malloc(size); 109219354Spjd if (newbuf == NULL) { 110219354Spjd pjdlog_error("Unable to decompress (no memory: %zu).", 111219354Spjd (size_t)size); 112219354Spjd return (NULL); 113219354Spjd } 114219354Spjd bzero(newbuf, size); 115219354Spjd *sizep = size; 116219354Spjd 117219354Spjd return (newbuf); 118219354Spjd} 119219354Spjd 120219354Spjd/* Minimum block size to try to compress. */ 121219354Spjd#define HAST_LZF_COMPRESS_MIN 1024 122219354Spjd 123219354Spjdstatic void * 124219354Spjdhast_lzf_compress(const unsigned char *data, size_t *sizep) 125219354Spjd{ 126219354Spjd unsigned char *newbuf; 127219354Spjd uint32_t origsize; 128219354Spjd size_t newsize; 129219354Spjd 130219354Spjd origsize = *sizep; 131219354Spjd 132219354Spjd if (origsize <= HAST_LZF_COMPRESS_MIN) 133219354Spjd return (NULL); 134219354Spjd 135219354Spjd newsize = sizeof(origsize) + origsize - HAST_LZF_COMPRESS_MIN; 136219354Spjd newbuf = malloc(newsize); 137219354Spjd if (newbuf == NULL) { 138219354Spjd pjdlog_warning("Unable to compress (no memory: %zu).", 139219354Spjd newsize); 140219354Spjd return (NULL); 141219354Spjd } 142219354Spjd newsize = lzf_compress(data, *sizep, newbuf + sizeof(origsize), 143219354Spjd newsize - sizeof(origsize)); 144219354Spjd if (newsize == 0) { 145219354Spjd free(newbuf); 146219354Spjd return (NULL); 147219354Spjd } 148219354Spjd origsize = htole32(origsize); 149219354Spjd bcopy(&origsize, newbuf, sizeof(origsize)); 150219354Spjd 151219354Spjd *sizep = sizeof(origsize) + newsize; 152219354Spjd return (newbuf); 153219354Spjd} 154219354Spjd 155219354Spjdstatic void * 156219354Spjdhast_lzf_decompress(const unsigned char *data, size_t *sizep) 157219354Spjd{ 158219354Spjd unsigned char *newbuf; 159219354Spjd uint32_t origsize; 160219354Spjd size_t newsize; 161219354Spjd 162219354Spjd PJDLOG_ASSERT(*sizep > sizeof(origsize)); 163219354Spjd 164219354Spjd bcopy(data, &origsize, sizeof(origsize)); 165219354Spjd origsize = le32toh(origsize); 166219354Spjd PJDLOG_ASSERT(origsize > HAST_LZF_COMPRESS_MIN); 167219354Spjd 168219354Spjd newbuf = malloc(origsize); 169219354Spjd if (newbuf == NULL) { 170219354Spjd pjdlog_error("Unable to decompress (no memory: %zu).", 171219354Spjd (size_t)origsize); 172219354Spjd return (NULL); 173219354Spjd } 174219354Spjd newsize = lzf_decompress(data + sizeof(origsize), 175219354Spjd *sizep - sizeof(origsize), newbuf, origsize); 176219354Spjd if (newsize == 0) { 177219354Spjd free(newbuf); 178219354Spjd pjdlog_error("Unable to decompress."); 179219354Spjd return (NULL); 180219354Spjd } 181219354Spjd PJDLOG_ASSERT(newsize == origsize); 182219354Spjd 183219354Spjd *sizep = newsize; 184219354Spjd return (newbuf); 185219354Spjd} 186219354Spjd 187219354Spjdconst char * 188219354Spjdcompression_name(int num) 189219354Spjd{ 190219354Spjd 191219354Spjd switch (num) { 192219354Spjd case HAST_COMPRESSION_NONE: 193219354Spjd return ("none"); 194219354Spjd case HAST_COMPRESSION_HOLE: 195219354Spjd return ("hole"); 196219354Spjd case HAST_COMPRESSION_LZF: 197219354Spjd return ("lzf"); 198219354Spjd } 199219354Spjd return ("unknown"); 200219354Spjd} 201219354Spjd 202219354Spjdint 203219354Spjdcompression_send(const struct hast_resource *res, struct nv *nv, void **datap, 204219354Spjd size_t *sizep, bool *freedatap) 205219354Spjd{ 206219354Spjd unsigned char *newbuf; 207219354Spjd int compression; 208219354Spjd size_t size; 209219354Spjd 210219354Spjd size = *sizep; 211219354Spjd compression = res->hr_compression; 212219354Spjd 213219354Spjd switch (compression) { 214219354Spjd case HAST_COMPRESSION_NONE: 215219354Spjd return (0); 216219354Spjd case HAST_COMPRESSION_HOLE: 217219354Spjd newbuf = hast_hole_compress(*datap, &size); 218219354Spjd break; 219219354Spjd case HAST_COMPRESSION_LZF: 220219354Spjd /* Try 'hole' compression first. */ 221219354Spjd newbuf = hast_hole_compress(*datap, &size); 222219354Spjd if (newbuf != NULL) 223219354Spjd compression = HAST_COMPRESSION_HOLE; 224219354Spjd else 225219354Spjd newbuf = hast_lzf_compress(*datap, &size); 226219354Spjd break; 227219354Spjd default: 228219354Spjd PJDLOG_ABORT("Invalid compression: %d.", res->hr_compression); 229219354Spjd } 230219354Spjd 231219354Spjd if (newbuf == NULL) { 232219354Spjd /* Unable to compress the data. */ 233219354Spjd return (0); 234219354Spjd } 235219354Spjd nv_add_string(nv, compression_name(compression), "compression"); 236219354Spjd if (nv_error(nv) != 0) { 237219354Spjd free(newbuf); 238219354Spjd errno = nv_error(nv); 239219354Spjd return (-1); 240219354Spjd } 241219354Spjd if (*freedatap) 242219354Spjd free(*datap); 243219354Spjd *freedatap = true; 244219354Spjd *datap = newbuf; 245219354Spjd *sizep = size; 246219354Spjd 247219354Spjd return (0); 248219354Spjd} 249219354Spjd 250219354Spjdint 251219354Spjdcompression_recv(const struct hast_resource *res __unused, struct nv *nv, 252219354Spjd void **datap, size_t *sizep, bool *freedatap) 253219354Spjd{ 254219354Spjd unsigned char *newbuf; 255219354Spjd const char *algo; 256219354Spjd size_t size; 257219354Spjd 258219354Spjd algo = nv_get_string(nv, "compression"); 259219354Spjd if (algo == NULL) 260219354Spjd return (0); /* No compression. */ 261219354Spjd 262219354Spjd newbuf = NULL; 263219354Spjd size = *sizep; 264219354Spjd 265219354Spjd if (strcmp(algo, "hole") == 0) 266219354Spjd newbuf = hast_hole_decompress(*datap, &size); 267219354Spjd else if (strcmp(algo, "lzf") == 0) 268219354Spjd newbuf = hast_lzf_decompress(*datap, &size); 269219354Spjd else { 270219354Spjd pjdlog_error("Unknown compression algorithm '%s'.", algo); 271219354Spjd return (-1); /* Unknown compression algorithm. */ 272219354Spjd } 273219354Spjd 274219354Spjd if (newbuf == NULL) 275219354Spjd return (-1); 276219354Spjd if (*freedatap) 277219354Spjd free(*datap); 278219354Spjd *freedatap = true; 279219354Spjd *datap = newbuf; 280219354Spjd *sizep = size; 281219354Spjd 282219354Spjd return (0); 283219354Spjd} 284