1 /*
2  ****************************************************************************
3  *                     -- GRASS Development Team --
4  *
5  * MODULE:      GRASS gis library
6  * FILENAME:    cmprlz4.c
7  * AUTHOR(S):   Eric G. Miller <egm2@jps.net>
8  *              Markus Metz
9  * PURPOSE:     To provide an interface to lz4 for compressing and
10  *              decompressing data using LZ4.  It's primary use is in
11  *              the storage and reading of GRASS floating point rasters.
12  *
13  * ALGORITHM:   https://code.google.com/p/lz4/
14  * DATE CREATED: Dec 18 2015
15  * COPYRIGHT:   (C) 2015 by the GRASS Development Team
16  *
17  *              This program is free software under the GNU General Public
18  *              License (version 2 or greater). Read the file COPYING that
19  *              comes with GRASS for details.
20  *
21  *****************************************************************************/
22 
23 /********************************************************************
24  * int                                                              *
25  * G_lz4_compress (src, srz_sz, dst, dst_sz)                        *
26  *     int src_sz, dst_sz;                                          *
27  *     unsigned char *src, *dst;                                    *
28  * ---------------------------------------------------------------- *
29  * This function is a wrapper around the LZ4 compression function.  *
30  * It uses an all or nothing call.                                  *
31  * If you need a continuous compression scheme, you'll have to code *
32  * your own.                                                        *
33  * In order to do a single pass compression, the input src must be  *
34  * copied to a buffer larger than the data.  This may cause         *
35  * performance degradation.                                         *
36  *                                                                  *
37  * The function either returns the number of bytes of compressed    *
38  * data in dst, or an error code.                                   *
39  *                                                                  *
40  * Errors include:                                                  *
41  *        -1 -- Compression failed.                                 *
42  *        -2 -- dst is too small.                                   *
43  *                                                                  *
44  * ================================================================ *
45  * int                                                              *
46  * G_lz4_expand (src, src_sz, dst, dst_sz)                          *
47  *     int src_sz, dst_sz;                                          *
48  *     unsigned char *src, *dst;                                    *
49  * ---------------------------------------------------------------- *
50  * This function is a wrapper around the lz4 decompression          *
51  * function.  It uses a single pass call.  If you need a continuous *
52  * expansion scheme, you'll have to code your own.                  *
53  *                                                                  *
54  * The function returns the number of bytes expanded into 'dst' or  *
55  * and error code.                                                  *
56  *                                                                  *
57  * Errors include:                                                  *
58  *        -1 -- Expansion failed.                                   *
59  *                                                                  *
60  ********************************************************************
61  */
62 
63 #include <grass/config.h>
64 
65 #include <grass/gis.h>
66 #include <grass/glocale.h>
67 
68 #include "lz4.h"
69 
70 int
G_lz4_compress_bound(int src_sz)71 G_lz4_compress_bound(int src_sz)
72 {
73     /* LZ4 has a fast version if destLen is large enough
74      * to hold a worst case result
75      */
76     return LZ4_compressBound(src_sz);
77 }
78 
79 int
G_lz4_compress(unsigned char * src,int src_sz,unsigned char * dst,int dst_sz)80 G_lz4_compress(unsigned char *src, int src_sz, unsigned char *dst,
81 		int dst_sz)
82 {
83     int err, nbytes, buf_sz;
84     unsigned char *buf;
85 
86     /* Catch errors early */
87     if (src == NULL || dst == NULL) {
88 	if (src == NULL)
89 	    G_warning(_("No source buffer"));
90 
91 	if (dst == NULL)
92 	    G_warning(_("No destination buffer"));
93 	return -1;
94     }
95 
96     /* Don't do anything if either of these are true */
97     if (src_sz <= 0 || dst_sz <= 0) {
98 	if (src_sz <= 0)
99 	    G_warning(_("Invalid source buffer size %d"), src_sz);
100 	if (dst_sz <= 0)
101 	    G_warning(_("Invalid destination buffer size %d"), dst_sz);
102 	return 0;
103     }
104 
105     /* Output buffer should be large enough for single pass compression */
106     buf = dst;
107     buf_sz = G_lz4_compress_bound(src_sz);
108     if (buf_sz > dst_sz) {
109 	G_warning("G_lz4_compress(): programmer error, destination is too small");
110 	if (NULL == (buf = (unsigned char *)
111 		     G_calloc(buf_sz, sizeof(unsigned char))))
112 	    return -1;
113     }
114     else
115 	buf_sz = dst_sz;
116 
117     /* Do single pass compression */
118     err = LZ4_compress_default((char *)src, (char *)buf, src_sz, buf_sz);
119 
120     if (err <= 0) {
121 	G_warning(_("LZ4 compression error"));
122 	if (buf != dst)
123 	    G_free(buf);
124 	return -1;
125     }
126     if (err >= src_sz) {
127 	/* compression not possible */
128 	if (buf != dst)
129 	    G_free(buf);
130 	return -2;
131     }
132 
133     /* bytes of compressed data is return value */
134     nbytes = err;
135 
136     if (buf != dst) {
137 	/* Copy the data from buf to dst */
138 	for (err = 0; err < nbytes; err++)
139 	    dst[err] = buf[err];
140 
141 	G_free(buf);
142     }
143 
144     return nbytes;
145 }
146 
147 int
G_lz4_expand(unsigned char * src,int src_sz,unsigned char * dst,int dst_sz)148 G_lz4_expand(unsigned char *src, int src_sz, unsigned char *dst,
149 	      int dst_sz)
150 {
151     int err, nbytes;
152 
153     /* Catch error condition */
154     if (src == NULL || dst == NULL) {
155 	if (src == NULL)
156 	    G_warning(_("No source buffer"));
157 
158 	if (dst == NULL)
159 	    G_warning(_("No destination buffer"));
160 	return -2;
161     }
162 
163     /* Don't do anything if either of these are true */
164     if (src_sz <= 0 || dst_sz <= 0) {
165 	if (src_sz <= 0)
166 	    G_warning(_("Invalid source buffer size %d"), src_sz);
167 	if (dst_sz <= 0)
168 	    G_warning(_("Invalid destination buffer size %d"), dst_sz);
169 	return 0;
170     }
171 
172     /* Do single pass decompress */
173     err = LZ4_decompress_safe((char *)src, (char *)dst, src_sz, dst_sz);
174     /* err = LZ4_decompress_fast(src, dst, src_sz); */
175 
176     if (err <= 0) {
177 	G_warning(_("LZ4 decompression error"));
178 	return -1;
179     }
180 
181     /* Number of bytes inflated to output stream is return value */
182     nbytes = err;
183 
184     if (nbytes != dst_sz) {
185 	/* TODO: it is not an error if destination is larger than needed */
186 	G_warning(_("Got uncompressed size %d, expected %d"), (int)nbytes, dst_sz);
187 	return -1;
188     }
189 
190     return nbytes;
191 }
192 
193 
194 /* vim: set softtabstop=4 shiftwidth=4 expandtab: */
195