blob: ba9302794f7418400dfb8c1baeec9d7026ca1bf6 [file] [log] [blame]
Muxi Yanc1f837c2017-05-04 18:17:13 -07001/*
2 *
Muxi Yan163d8d62017-06-30 09:53:37 -07003 * Copyright 2017 gRPC authors.
Muxi Yanc1f837c2017-05-04 18:17:13 -07004 *
Muxi Yan163d8d62017-06-30 09:53:37 -07005 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
Muxi Yanc1f837c2017-05-04 18:17:13 -07008 *
Muxi Yan163d8d62017-06-30 09:53:37 -07009 * http://www.apache.org/licenses/LICENSE-2.0
Muxi Yanc1f837c2017-05-04 18:17:13 -070010 *
Muxi Yan163d8d62017-06-30 09:53:37 -070011 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
Muxi Yanc1f837c2017-05-04 18:17:13 -070016 *
17 */
18
19#include <grpc/support/alloc.h>
20#include <grpc/support/log.h>
21
Muxi Yane87a7e12017-06-29 16:53:24 -070022#include "src/core/lib/compression/stream_compression.h"
Muxi Yanc1f837c2017-05-04 18:17:13 -070023#include "src/core/lib/iomgr/exec_ctx.h"
24#include "src/core/lib/slice/slice_internal.h"
25
26#define OUTPUT_BLOCK_SIZE (1024)
27
28static bool gzip_flate(grpc_stream_compression_context *ctx,
29 grpc_slice_buffer *in, grpc_slice_buffer *out,
30 size_t *output_size, size_t max_output_size, int flush,
31 bool *end_of_context) {
32 GPR_ASSERT(flush == 0 || flush == Z_SYNC_FLUSH || flush == Z_FINISH);
33 /* Full flush is not allowed when inflating. */
34 GPR_ASSERT(!(ctx->flate == inflate && (flush == Z_FINISH)));
35
36 grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
37 int r;
38 bool eoc = false;
39 size_t original_max_output_size = max_output_size;
40 while (max_output_size > 0 && (in->length > 0 || flush) && !eoc) {
41 size_t slice_size = max_output_size < OUTPUT_BLOCK_SIZE ? max_output_size
42 : OUTPUT_BLOCK_SIZE;
43 grpc_slice slice_out = GRPC_SLICE_MALLOC(slice_size);
44 ctx->zs.avail_out = (uInt)slice_size;
45 ctx->zs.next_out = GRPC_SLICE_START_PTR(slice_out);
46 while (ctx->zs.avail_out > 0 && in->length > 0 && !eoc) {
47 grpc_slice slice = grpc_slice_buffer_take_first(in);
48 ctx->zs.avail_in = (uInt)GRPC_SLICE_LENGTH(slice);
49 ctx->zs.next_in = GRPC_SLICE_START_PTR(slice);
50 r = ctx->flate(&ctx->zs, Z_NO_FLUSH);
51 if (r < 0 && r != Z_BUF_ERROR) {
52 gpr_log(GPR_ERROR, "zlib error (%d)", r);
53 grpc_slice_unref_internal(&exec_ctx, slice_out);
54 grpc_exec_ctx_finish(&exec_ctx);
55 return false;
56 } else if (r == Z_STREAM_END && ctx->flate == inflate) {
57 eoc = true;
58 }
59 if (ctx->zs.avail_in > 0) {
60 grpc_slice_buffer_undo_take_first(
61 in,
62 grpc_slice_sub(slice, GRPC_SLICE_LENGTH(slice) - ctx->zs.avail_in,
63 GRPC_SLICE_LENGTH(slice)));
64 }
65 grpc_slice_unref_internal(&exec_ctx, slice);
66 }
67 if (flush != 0 && ctx->zs.avail_out > 0 && !eoc) {
68 GPR_ASSERT(in->length == 0);
69 r = ctx->flate(&ctx->zs, flush);
70 if (flush == Z_SYNC_FLUSH) {
71 switch (r) {
72 case Z_OK:
73 /* Maybe flush is not complete; just made some partial progress. */
74 if (ctx->zs.avail_out > 0) {
75 flush = 0;
76 }
77 break;
78 case Z_BUF_ERROR:
79 case Z_STREAM_END:
80 flush = 0;
81 break;
82 default:
83 gpr_log(GPR_ERROR, "zlib error (%d)", r);
84 grpc_slice_unref_internal(&exec_ctx, slice_out);
85 grpc_exec_ctx_finish(&exec_ctx);
86 return false;
87 }
88 } else if (flush == Z_FINISH) {
89 switch (r) {
90 case Z_OK:
91 case Z_BUF_ERROR:
92 /* Wait for the next loop to assign additional output space. */
93 GPR_ASSERT(ctx->zs.avail_out == 0);
94 break;
95 case Z_STREAM_END:
96 flush = 0;
97 break;
98 default:
99 gpr_log(GPR_ERROR, "zlib error (%d)", r);
100 grpc_slice_unref_internal(&exec_ctx, slice_out);
101 grpc_exec_ctx_finish(&exec_ctx);
102 return false;
103 }
104 }
105 }
106
107 if (ctx->zs.avail_out == 0) {
108 grpc_slice_buffer_add(out, slice_out);
109 } else if (ctx->zs.avail_out < slice_size) {
110 slice_out.data.refcounted.length -= ctx->zs.avail_out;
111 grpc_slice_buffer_add(out, slice_out);
112 } else {
113 grpc_slice_unref_internal(&exec_ctx, slice_out);
114 }
115 max_output_size -= (slice_size - ctx->zs.avail_out);
116 }
117 grpc_exec_ctx_finish(&exec_ctx);
118 if (end_of_context) {
119 *end_of_context = eoc;
120 }
121 if (output_size) {
122 *output_size = original_max_output_size - max_output_size;
123 }
124 return true;
125}
126
127bool grpc_stream_compress(grpc_stream_compression_context *ctx,
128 grpc_slice_buffer *in, grpc_slice_buffer *out,
129 size_t *output_size, size_t max_output_size,
130 grpc_stream_compression_flush flush) {
131 GPR_ASSERT(ctx->flate == deflate);
132 int gzip_flush;
133 switch (flush) {
134 case GRPC_STREAM_COMPRESSION_FLUSH_NONE:
135 gzip_flush = 0;
136 break;
137 case GRPC_STREAM_COMPRESSION_FLUSH_SYNC:
138 gzip_flush = Z_SYNC_FLUSH;
139 break;
140 case GRPC_STREAM_COMPRESSION_FLUSH_FINISH:
141 gzip_flush = Z_FINISH;
142 break;
143 default:
144 gzip_flush = 0;
145 }
146 return gzip_flate(ctx, in, out, output_size, max_output_size, gzip_flush,
147 NULL);
148}
149
150bool grpc_stream_decompress(grpc_stream_compression_context *ctx,
151 grpc_slice_buffer *in, grpc_slice_buffer *out,
152 size_t *output_size, size_t max_output_size,
153 bool *end_of_context) {
154 GPR_ASSERT(ctx->flate == inflate);
155 return gzip_flate(ctx, in, out, output_size, max_output_size, Z_SYNC_FLUSH,
156 end_of_context);
157}
158
159grpc_stream_compression_context *grpc_stream_compression_context_create(
160 grpc_stream_compression_method method) {
161 grpc_stream_compression_context *ctx =
Yash Tibrewal52778c42017-09-11 15:00:11 -0700162 (grpc_stream_compression_context *)gpr_zalloc(
163 sizeof(grpc_stream_compression_context));
Muxi Yanc1f837c2017-05-04 18:17:13 -0700164 int r;
165 if (ctx == NULL) {
166 return NULL;
167 }
168 if (method == GRPC_STREAM_COMPRESSION_DECOMPRESS) {
169 r = inflateInit2(&ctx->zs, 0x1F);
170 ctx->flate = inflate;
171 } else {
172 r = deflateInit2(&ctx->zs, Z_DEFAULT_COMPRESSION, Z_DEFLATED, 0x1F, 8,
173 Z_DEFAULT_STRATEGY);
174 ctx->flate = deflate;
175 }
176 if (r != Z_OK) {
177 gpr_free(ctx);
178 return NULL;
179 }
180
181 return ctx;
182}
183
184void grpc_stream_compression_context_destroy(
185 grpc_stream_compression_context *ctx) {
186 if (ctx->flate == inflate) {
187 inflateEnd(&ctx->zs);
188 } else {
189 deflateEnd(&ctx->zs);
190 }
191 gpr_free(ctx);
192}