blob: c3a21ec19f7528bc49d3ad904e71ce106fc88248 [file] [log] [blame]
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -08001/*
2 *
Craig Tiller8a9fd522016-03-25 17:09:29 -07003 * Copyright 2015-2016, Google Inc.
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -08004 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions are
8 * met:
9 *
10 * * Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * * Redistributions in binary form must reproduce the above
13 * copyright notice, this list of conditions and the following disclaimer
14 * in the documentation and/or other materials provided with the
15 * distribution.
16 * * Neither the name of Google Inc. nor the names of its
17 * contributors may be used to endorse or promote products derived from
18 * this software without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
24 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 *
32 */
33
34#include "src/core/compression/message_compress.h"
35
36#include <string.h>
37
38#include <grpc/support/alloc.h>
39#include <grpc/support/log.h>
40
41#include <zlib.h>
42
43#define OUTPUT_BLOCK_SIZE 1024
44
Craig Tillera82950e2015-09-22 12:33:20 -070045static int zlib_body(z_stream* zs, gpr_slice_buffer* input,
46 gpr_slice_buffer* output,
47 int (*flate)(z_stream* zs, int flush)) {
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -080048 int r;
49 int flush;
50 size_t i;
Craig Tillera82950e2015-09-22 12:33:20 -070051 gpr_slice outbuf = gpr_slice_malloc(OUTPUT_BLOCK_SIZE);
52 const uInt uint_max = ~(uInt)0;
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -080053
Craig Tillera82950e2015-09-22 12:33:20 -070054 GPR_ASSERT(GPR_SLICE_LENGTH(outbuf) <= uint_max);
55 zs->avail_out = (uInt)GPR_SLICE_LENGTH(outbuf);
56 zs->next_out = GPR_SLICE_START_PTR(outbuf);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -080057 flush = Z_NO_FLUSH;
Craig Tillera82950e2015-09-22 12:33:20 -070058 for (i = 0; i < input->count; i++) {
59 if (i == input->count - 1) flush = Z_FINISH;
60 GPR_ASSERT(GPR_SLICE_LENGTH(input->slices[i]) <= uint_max);
61 zs->avail_in = (uInt)GPR_SLICE_LENGTH(input->slices[i]);
62 zs->next_in = GPR_SLICE_START_PTR(input->slices[i]);
63 do {
64 if (zs->avail_out == 0) {
65 gpr_slice_buffer_add_indexed(output, outbuf);
66 outbuf = gpr_slice_malloc(OUTPUT_BLOCK_SIZE);
67 GPR_ASSERT(GPR_SLICE_LENGTH(outbuf) <= uint_max);
68 zs->avail_out = (uInt)GPR_SLICE_LENGTH(outbuf);
69 zs->next_out = GPR_SLICE_START_PTR(outbuf);
70 }
71 r = flate(zs, flush);
David Garcia Quintas5e0da582015-12-10 18:27:32 -080072 if (r < 0 && r != Z_BUF_ERROR /* not fatal */) {
73 gpr_log(GPR_INFO, "zlib error (%d)", r);
Craig Tillera82950e2015-09-22 12:33:20 -070074 goto error;
75 }
76 } while (zs->avail_out == 0);
77 if (zs->avail_in) {
78 gpr_log(GPR_INFO, "zlib: not all input consumed");
79 goto error;
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -080080 }
Craig Tillera82950e2015-09-22 12:33:20 -070081 }
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -080082
Craig Tillera82950e2015-09-22 12:33:20 -070083 GPR_ASSERT(outbuf.refcount);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -080084 outbuf.data.refcounted.length -= zs->avail_out;
Craig Tillera82950e2015-09-22 12:33:20 -070085 gpr_slice_buffer_add_indexed(output, outbuf);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -080086
87 return 1;
88
89error:
Craig Tillera82950e2015-09-22 12:33:20 -070090 gpr_slice_unref(outbuf);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -080091 return 0;
92}
93
Craig Tiller620e9652015-12-14 12:02:50 -080094static void* zalloc_gpr(void* opaque, unsigned int items, unsigned int size) {
David Garcia Quintas0a087912015-12-09 10:46:14 -080095 return gpr_malloc(items * size);
96}
97
Craig Tiller620e9652015-12-14 12:02:50 -080098static void zfree_gpr(void* opaque, void* address) { gpr_free(address); }
David Garcia Quintas0a087912015-12-09 10:46:14 -080099
Craig Tillera82950e2015-09-22 12:33:20 -0700100static int zlib_compress(gpr_slice_buffer* input, gpr_slice_buffer* output,
101 int gzip) {
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800102 z_stream zs;
103 int r;
104 size_t i;
105 size_t count_before = output->count;
106 size_t length_before = output->length;
Craig Tillera82950e2015-09-22 12:33:20 -0700107 memset(&zs, 0, sizeof(zs));
David Garcia Quintas0a087912015-12-09 10:46:14 -0800108 zs.zalloc = zalloc_gpr;
109 zs.zfree = zfree_gpr;
Craig Tillera82950e2015-09-22 12:33:20 -0700110 r = deflateInit2(&zs, Z_DEFAULT_COMPRESSION, Z_DEFLATED, 15 | (gzip ? 16 : 0),
111 8, Z_DEFAULT_STRATEGY);
David Garcia Quintase9fa3112015-12-09 17:25:10 -0800112 GPR_ASSERT(r == Z_OK);
Craig Tillera82950e2015-09-22 12:33:20 -0700113 r = zlib_body(&zs, input, output, deflate) && output->length < input->length;
114 if (!r) {
115 for (i = count_before; i < output->count; i++) {
116 gpr_slice_unref(output->slices[i]);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800117 }
Craig Tillera82950e2015-09-22 12:33:20 -0700118 output->count = count_before;
119 output->length = length_before;
120 }
121 deflateEnd(&zs);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800122 return r;
123}
124
Craig Tillera82950e2015-09-22 12:33:20 -0700125static int zlib_decompress(gpr_slice_buffer* input, gpr_slice_buffer* output,
126 int gzip) {
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800127 z_stream zs;
128 int r;
129 size_t i;
130 size_t count_before = output->count;
131 size_t length_before = output->length;
Craig Tillera82950e2015-09-22 12:33:20 -0700132 memset(&zs, 0, sizeof(zs));
David Garcia Quintas0a087912015-12-09 10:46:14 -0800133 zs.zalloc = zalloc_gpr;
134 zs.zfree = zfree_gpr;
Craig Tillera82950e2015-09-22 12:33:20 -0700135 r = inflateInit2(&zs, 15 | (gzip ? 16 : 0));
David Garcia Quintase9fa3112015-12-09 17:25:10 -0800136 GPR_ASSERT(r == Z_OK);
Craig Tillera82950e2015-09-22 12:33:20 -0700137 r = zlib_body(&zs, input, output, inflate);
138 if (!r) {
139 for (i = count_before; i < output->count; i++) {
140 gpr_slice_unref(output->slices[i]);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800141 }
Craig Tillera82950e2015-09-22 12:33:20 -0700142 output->count = count_before;
143 output->length = length_before;
144 }
145 inflateEnd(&zs);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800146 return r;
147}
148
Craig Tillera82950e2015-09-22 12:33:20 -0700149static int copy(gpr_slice_buffer* input, gpr_slice_buffer* output) {
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800150 size_t i;
Craig Tillera82950e2015-09-22 12:33:20 -0700151 for (i = 0; i < input->count; i++) {
152 gpr_slice_buffer_add(output, gpr_slice_ref(input->slices[i]));
153 }
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800154 return 1;
155}
156
David Garcia Quintas0a087912015-12-09 10:46:14 -0800157static int compress_inner(grpc_compression_algorithm algorithm,
Craig Tiller620e9652015-12-14 12:02:50 -0800158 gpr_slice_buffer* input, gpr_slice_buffer* output) {
Craig Tillera82950e2015-09-22 12:33:20 -0700159 switch (algorithm) {
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800160 case GRPC_COMPRESS_NONE:
161 /* the fallback path always needs to be send uncompressed: we simply
162 rely on that here */
163 return 0;
164 case GRPC_COMPRESS_DEFLATE:
Craig Tillera82950e2015-09-22 12:33:20 -0700165 return zlib_compress(input, output, 0);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800166 case GRPC_COMPRESS_GZIP:
Craig Tillera82950e2015-09-22 12:33:20 -0700167 return zlib_compress(input, output, 1);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800168 case GRPC_COMPRESS_ALGORITHMS_COUNT:
169 break;
Craig Tillera82950e2015-09-22 12:33:20 -0700170 }
171 gpr_log(GPR_ERROR, "invalid compression algorithm %d", algorithm);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800172 return 0;
173}
174
Craig Tillera82950e2015-09-22 12:33:20 -0700175int grpc_msg_compress(grpc_compression_algorithm algorithm,
176 gpr_slice_buffer* input, gpr_slice_buffer* output) {
177 if (!compress_inner(algorithm, input, output)) {
178 copy(input, output);
179 return 0;
180 }
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800181 return 1;
182}
183
Craig Tillera82950e2015-09-22 12:33:20 -0700184int grpc_msg_decompress(grpc_compression_algorithm algorithm,
185 gpr_slice_buffer* input, gpr_slice_buffer* output) {
186 switch (algorithm) {
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800187 case GRPC_COMPRESS_NONE:
Craig Tillera82950e2015-09-22 12:33:20 -0700188 return copy(input, output);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800189 case GRPC_COMPRESS_DEFLATE:
Craig Tillera82950e2015-09-22 12:33:20 -0700190 return zlib_decompress(input, output, 0);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800191 case GRPC_COMPRESS_GZIP:
Craig Tillera82950e2015-09-22 12:33:20 -0700192 return zlib_decompress(input, output, 1);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800193 case GRPC_COMPRESS_ALGORITHMS_COUNT:
194 break;
Craig Tillera82950e2015-09-22 12:33:20 -0700195 }
196 gpr_log(GPR_ERROR, "invalid compression algorithm %d", algorithm);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800197 return 0;
Craig Tiller190d3602015-02-18 09:23:38 -0800198}