1 /* Copyright (c) 2000, 2010, Oracle and/or its affiliates. All rights reserved.
2 
3    This program is free software; you can redistribute it and/or modify
4    it under the terms of the GNU General Public License as published by
5    the Free Software Foundation; version 2 of the License.
6 
7    This program is distributed in the hope that it will be useful,
8    but WITHOUT ANY WARRANTY; without even the implied warranty of
9    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10    GNU General Public License for more details.
11 
12    You should have received a copy of the GNU General Public License
13    along with this program; if not, write to the Free Software
14    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1335  USA */
15 
16 /* Written by Sinisa Milivojevic <sinisa@mysql.com> */
17 
18 #include <my_global.h>
19 #ifdef HAVE_COMPRESS
20 #include <my_sys.h>
21 #ifndef SCO
22 #include <m_string.h>
23 #endif
24 #include <zlib.h>
25 
26 /*
27    This replaces the packet with a compressed packet
28 
29    SYNOPSIS
30      my_compress()
31      packet	Data to compress. This is is replaced with the compressed data.
32      len	Length of data to compress at 'packet'
33      complen	out: 0 if packet was not compressed
34 
35    RETURN
36      1   error. 'len' is not changed'
37      0   ok.  In this case 'len' contains the size of the compressed packet
38 */
39 
my_compress(uchar * packet,size_t * len,size_t * complen)40 my_bool my_compress(uchar *packet, size_t *len, size_t *complen)
41 {
42   DBUG_ENTER("my_compress");
43   if (*len < MIN_COMPRESS_LENGTH)
44   {
45     *complen=0;
46     DBUG_PRINT("note",("Packet too short: Not compressed"));
47   }
48   else
49   {
50     uchar *compbuf=my_compress_alloc(packet,len,complen);
51     if (!compbuf)
52       DBUG_RETURN(*complen ? 0 : 1);
53     memcpy(packet,compbuf,*len);
54     my_free(compbuf);
55   }
56   DBUG_RETURN(0);
57 }
58 
59 
60 /*
61   Valgrind normally gives false alarms for zlib operations, in the form of
62   "conditional jump depends on uninitialised values" etc. The reason is
63   explained in the zlib FAQ (http://www.zlib.net/zlib_faq.html#faq36):
64 
65     "That is intentional for performance reasons, and the output of deflate
66     is not affected."
67 
68   Also discussed on a blog
69   (http://www.sirena.org.uk/log/2006/02/19/zlib-generating-valgrind-warnings/):
70 
71     "...loop unrolling in the zlib library causes the mentioned
72     “Conditional jump or move depends on uninitialised value(s)”
73     warnings. These are safe since the results of the comparison are
74     subsequently ignored..."
75 
76     "the results of the calculations are discarded by bounds checking done
77     after the loop exits"
78 
79   Fix by initializing the memory allocated by zlib when running under Valgrind.
80 
81   This fix is safe, since such memory is only used internally by zlib, so we
82   will not hide any bugs in mysql this way.
83 */
my_az_allocator(void * dummy,unsigned int items,unsigned int size)84 void *my_az_allocator(void *dummy __attribute__((unused)), unsigned int items,
85                       unsigned int size)
86 {
87   return my_malloc((size_t)items*(size_t)size, IF_VALGRIND(MY_ZEROFILL, MYF(0)));
88 }
89 
my_az_free(void * dummy,void * address)90 void my_az_free(void *dummy __attribute__((unused)), void *address)
91 {
92   my_free(address);
93 }
94 
95 /*
96   This works like zlib compress(), but using custom memory allocators to work
97   better with my_malloc leak detection and Valgrind.
98 */
my_compress_buffer(uchar * dest,size_t * destLen,const uchar * source,size_t sourceLen)99 int my_compress_buffer(uchar *dest, size_t *destLen,
100                        const uchar *source, size_t sourceLen)
101 {
102     z_stream stream;
103     int err;
104 
105     stream.next_in = (Bytef*)source;
106     stream.avail_in = (uInt)sourceLen;
107     stream.next_out = (Bytef*)dest;
108     stream.avail_out = (uInt)*destLen;
109     if ((size_t)stream.avail_out != *destLen)
110       return Z_BUF_ERROR;
111 
112     stream.zalloc = (alloc_func)my_az_allocator;
113     stream.zfree = (free_func)my_az_free;
114     stream.opaque = (voidpf)0;
115 
116     err = deflateInit(&stream, Z_DEFAULT_COMPRESSION);
117     if (err != Z_OK) return err;
118 
119     err = deflate(&stream, Z_FINISH);
120     if (err != Z_STREAM_END) {
121         deflateEnd(&stream);
122         return err == Z_OK ? Z_BUF_ERROR : err;
123     }
124     *destLen = stream.total_out;
125 
126     err = deflateEnd(&stream);
127     return err;
128 }
129 
my_compress_alloc(const uchar * packet,size_t * len,size_t * complen)130 uchar *my_compress_alloc(const uchar *packet, size_t *len, size_t *complen)
131 {
132   uchar *compbuf;
133   int res;
134   *complen=  *len * 120 / 100 + 12;
135 
136   if (!(compbuf= (uchar *) my_malloc(*complen, MYF(MY_WME))))
137     return 0;					/* Not enough memory */
138 
139   res= my_compress_buffer(compbuf, complen, packet, *len);
140 
141   if (res != Z_OK)
142   {
143     my_free(compbuf);
144     return 0;
145   }
146 
147   if (*complen >= *len)
148   {
149     *complen= 0;
150     my_free(compbuf);
151     DBUG_PRINT("note",("Packet got longer on compression; Not compressed"));
152     return 0;
153   }
154   /* Store length of compressed packet in *len */
155   swap_variables(size_t, *len, *complen);
156   return compbuf;
157 }
158 
159 
160 /*
161   Uncompress packet
162 
163    SYNOPSIS
164      my_uncompress()
165      packet	Compressed data. This is is replaced with the original data.
166      len	Length of compressed data
167      complen	Length of the packet buffer (must be enough for the original
168 	        data)
169 
170    RETURN
171      1   error
172      0   ok.  In this case 'complen' contains the updated size of the
173               real data.
174 */
175 
my_uncompress(uchar * packet,size_t len,size_t * complen)176 my_bool my_uncompress(uchar *packet, size_t len, size_t *complen)
177 {
178   uLongf tmp_complen;
179   DBUG_ENTER("my_uncompress");
180 
181   if (*complen)					/* If compressed */
182   {
183     uchar *compbuf= (uchar *) my_malloc(*complen,MYF(MY_WME));
184     int error;
185     if (!compbuf)
186       DBUG_RETURN(1);				/* Not enough memory */
187 
188     tmp_complen= (uLongf) *complen;
189     error= uncompress((Bytef*) compbuf, &tmp_complen, (Bytef*) packet,
190                       (uLong) len);
191     *complen= tmp_complen;
192     if (error != Z_OK)
193     {						/* Probably wrong packet */
194       DBUG_PRINT("error",("Can't uncompress packet, error: %d",error));
195       my_free(compbuf);
196       DBUG_RETURN(1);
197     }
198     memcpy(packet, compbuf, *complen);
199     my_free(compbuf);
200   }
201   else
202     *complen= len;
203   DBUG_RETURN(0);
204 }
205 
206 #endif /* HAVE_COMPRESS */
207