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-1301 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
40my_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*/
84void *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
90void 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*/
99int 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
130uchar *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
176my_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