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 | |
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 | */ |
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 | |
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 | */ |
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 | |
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 | |
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 | |